diff options
Diffstat (limited to 'src')
256 files changed, 54879 insertions, 256 deletions
diff --git a/src/base.cpp b/src/base.cpp index ef6ff5ccf..9c002773b 100644 --- a/src/base.cpp +++ b/src/base.cpp @@ -1 +1,95 @@ -/* +------------------------------------+
* | 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_config.h"
#include "base.h"
#include <time.h>
#include "inspircd.h"
const int bitfields[] = {1,2,4,8,16,32,64,128};
const int inverted_bitfields[] = {~1,~2,~4,~8,~16,~32,~64,~128};
classbase::classbase()
{
this->age = time(NULL);
}
bool Extensible::Shrink(const std::string &key)
{
/* map::size_type map::erase( const key_type& key );
* returns the number of elements removed, std::map
* is single-associative so this should only be 0 or 1
*/
return this->Extension_Items.erase(key);
}
void Extensible::GetExtList(std::deque<std::string> &list)
{
for (ExtensibleStore::iterator u = Extension_Items.begin(); u != Extension_Items.end(); u++)
{
list.push_back(u->first);
}
}
void BoolSet::Set(int number)
{
this->bits |= bitfields[number];
}
void BoolSet::Unset(int number)
{
this->bits &= inverted_bitfields[number];
}
void BoolSet::Invert(int number)
{
this->bits ^= bitfields[number];
}
bool BoolSet::Get(int number)
{
return ((this->bits | bitfields[number]) > 0);
}
bool BoolSet::operator==(BoolSet other)
{
return (this->bits == other.bits);
}
BoolSet BoolSet::operator|(BoolSet other)
{
BoolSet x(this->bits | other.bits);
return x;
}
BoolSet BoolSet::operator&(BoolSet other)
{
BoolSet x(this->bits & other.bits);
return x;
}
BoolSet::BoolSet()
{
this->bits = 0;
}
BoolSet::BoolSet(char bitmask)
{
this->bits = bitmask;
}
bool BoolSet::operator=(BoolSet other)
{
this->bits = other.bits;
return true;
}
\ No newline at end of file +/* +------------------------------------+ + * | 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_config.h" +#include "base.h" +#include <time.h> +#include "inspircd.h" + +const int bitfields[] = {1,2,4,8,16,32,64,128}; +const int inverted_bitfields[] = {~1,~2,~4,~8,~16,~32,~64,~128}; + +classbase::classbase() +{ + this->age = time(NULL); +} + +bool Extensible::Shrink(const std::string &key) +{ + /* map::size_type map::erase( const key_type& key ); + * returns the number of elements removed, std::map + * is single-associative so this should only be 0 or 1 + */ + return this->Extension_Items.erase(key); +} + +void Extensible::GetExtList(std::deque<std::string> &list) +{ + for (ExtensibleStore::iterator u = Extension_Items.begin(); u != Extension_Items.end(); u++) + { + list.push_back(u->first); + } +} + +void BoolSet::Set(int number) +{ + this->bits |= bitfields[number]; +} + +void BoolSet::Unset(int number) +{ + this->bits &= inverted_bitfields[number]; +} + +void BoolSet::Invert(int number) +{ + this->bits ^= bitfields[number]; +} + +bool BoolSet::Get(int number) +{ + return ((this->bits | bitfields[number]) > 0); +} + +bool BoolSet::operator==(BoolSet other) +{ + return (this->bits == other.bits); +} + +BoolSet BoolSet::operator|(BoolSet other) +{ + BoolSet x(this->bits | other.bits); + return x; +} + +BoolSet BoolSet::operator&(BoolSet other) +{ + BoolSet x(this->bits & other.bits); + return x; +} + +BoolSet::BoolSet() +{ + this->bits = 0; +} + +BoolSet::BoolSet(char bitmask) +{ + this->bits = bitmask; +} + +bool BoolSet::operator=(BoolSet other) +{ + this->bits = other.bits; + return true; +} diff --git a/src/channels.cpp b/src/channels.cpp index 74543925e..b44a863b4 100644 --- a/src/channels.cpp +++ b/src/channels.cpp @@ -1 +1,1067 @@ -/* +------------------------------------+
* | 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 <stdarg.h>
#include "configreader.h"
#include "users.h"
#include "modules.h"
#include "wildcard.h"
#include "mode.h"
chanrec::chanrec(InspIRCd* Instance) : ServerInstance(Instance)
{
*name = *topic = *setby = *key = 0;
maxbans = created = topicset = limit = 0;
memset(&modes,0,64);
age = ServerInstance->Time(true);
}
void chanrec::SetMode(char mode,bool mode_on)
{
modes[mode-65] = mode_on;
if (!mode_on)
this->SetModeParam(mode,"",false);
}
void chanrec::SetModeParam(char mode,const char* parameter,bool mode_on)
{
CustomModeList::iterator n = custom_mode_params.find(mode);
if (mode_on)
{
if (n == custom_mode_params.end())
custom_mode_params[mode] = strdup(parameter);
}
else
{
if (n != custom_mode_params.end())
{
free(n->second);
custom_mode_params.erase(n);
}
}
}
bool chanrec::IsModeSet(char mode)
{
return modes[mode-65];
}
std::string chanrec::GetModeParameter(char mode)
{
switch (mode)
{
case 'k':
return this->key;
case 'l':
return ConvToStr(this->limit);
default:
CustomModeList::iterator n = custom_mode_params.find(mode);
if (n != custom_mode_params.end())
return n->second;
return "";
break;
}
}
long chanrec::GetUserCounter()
{
return (this->internal_userlist.size());
}
void chanrec::AddUser(userrec* user)
{
internal_userlist[user] = user->nick;
}
unsigned long chanrec::DelUser(userrec* user)
{
CUListIter a = internal_userlist.find(user);
if (a != internal_userlist.end())
{
internal_userlist.erase(a);
/* And tidy any others... */
DelOppedUser(user);
DelHalfoppedUser(user);
DelVoicedUser(user);
}
return internal_userlist.size();
}
bool chanrec::HasUser(userrec* user)
{
return (internal_userlist.find(user) != internal_userlist.end());
}
void chanrec::AddOppedUser(userrec* user)
{
internal_op_userlist[user] = user->nick;
}
void chanrec::DelOppedUser(userrec* user)
{
CUListIter a = internal_op_userlist.find(user);
if (a != internal_op_userlist.end())
{
internal_op_userlist.erase(a);
return;
}
}
void chanrec::AddHalfoppedUser(userrec* user)
{
internal_halfop_userlist[user] = user->nick;
}
void chanrec::DelHalfoppedUser(userrec* user)
{
CUListIter a = internal_halfop_userlist.find(user);
if (a != internal_halfop_userlist.end())
{
internal_halfop_userlist.erase(a);
}
}
void chanrec::AddVoicedUser(userrec* user)
{
internal_voice_userlist[user] = user->nick;
}
void chanrec::DelVoicedUser(userrec* user)
{
CUListIter a = internal_voice_userlist.find(user);
if (a != internal_voice_userlist.end())
{
internal_voice_userlist.erase(a);
}
}
CUList* chanrec::GetUsers()
{
return &internal_userlist;
}
CUList* chanrec::GetOppedUsers()
{
return &internal_op_userlist;
}
CUList* chanrec::GetHalfoppedUsers()
{
return &internal_halfop_userlist;
}
CUList* chanrec::GetVoicedUsers()
{
return &internal_voice_userlist;
}
void chanrec::SetDefaultModes()
{
irc::spacesepstream list(ServerInstance->Config->DefaultModes);
std::string modeseq = list.GetToken();
std::string parameter;
userrec* dummyuser = new userrec(ServerInstance);
dummyuser->SetFd(FD_MAGIC_NUMBER);
for (std::string::iterator n = modeseq.begin(); n != modeseq.end(); ++n)
{
ModeHandler* mode = ServerInstance->Modes->FindMode(*n, MODETYPE_CHANNEL);
if (mode)
{
if (mode->GetNumParams(true))
parameter = list.GetToken().c_str();
else
parameter.clear();
mode->OnModeChange(dummyuser, dummyuser, this, parameter, true);
}
}
delete dummyuser;
}
/*
* add a channel to a user, creating the record for it if needed and linking
* it to the user record
*/
chanrec* chanrec::JoinUser(InspIRCd* Instance, userrec *user, const char* cn, bool override, const char* key, time_t TS)
{
if (!user || !cn)
return NULL;
bool new_channel = false;
char cname[MAXBUF];
int MOD_RESULT = 0;
strlcpy(cname,cn,CHANMAX);
std::string privs;
chanrec* Ptr = Instance->FindChan(cname);
if (!Ptr)
{
if ((!IS_LOCAL(user)) && (!TS))
Instance->Log(DEBUG,"*** BUG *** chanrec::JoinUser called for REMOTE user '%s' on channel '%s' but no TS given!", user->nick, cn);
privs = "@";
if (IS_LOCAL(user) && override == false)
{
MOD_RESULT = 0;
FOREACH_RESULT_I(Instance,I_OnUserPreJoin,OnUserPreJoin(user,NULL,cname,privs));
if (MOD_RESULT == 1)
return NULL;
}
/* create a new one */
Ptr = new chanrec(Instance);
(*(Instance->chanlist))[cname] = Ptr;
strlcpy(Ptr->name, cname,CHANMAX);
/* As spotted by jilles, dont bother to set this on remote users */
if (IS_LOCAL(user))
Ptr->SetDefaultModes();
Ptr->created = TS ? TS : Instance->Time();
Ptr->age = Ptr->created;
*Ptr->topic = 0;
*Ptr->setby = 0;
Ptr->topicset = 0;
new_channel = true;
}
else
{
/* Already on the channel */
if (Ptr->HasUser(user))
return NULL;
/*
* remote users are allowed us to bypass channel modes
* and bans (used by servers)
*/
if (IS_LOCAL(user) && override == false)
{
MOD_RESULT = 0;
FOREACH_RESULT_I(Instance,I_OnUserPreJoin,OnUserPreJoin(user,Ptr,cname,privs));
if (MOD_RESULT == 1)
{
return NULL;
}
else if (MOD_RESULT == 0)
{
if (*Ptr->key)
{
MOD_RESULT = 0;
FOREACH_RESULT_I(Instance,I_OnCheckKey,OnCheckKey(user, Ptr, key ? key : ""));
if (!MOD_RESULT)
{
if ((!key) || strcmp(key,Ptr->key))
{
user->WriteServ("475 %s %s :Cannot join channel (Incorrect channel key)",user->nick, Ptr->name);
return NULL;
}
}
}
if (Ptr->modes[CM_INVITEONLY])
{
MOD_RESULT = 0;
FOREACH_RESULT_I(Instance,I_OnCheckInvite,OnCheckInvite(user, Ptr));
if (!MOD_RESULT)
{
if (user->IsInvited(Ptr->name))
{
/* user was invited to channel */
/* there may be an optional channel NOTICE here */
}
else
{
user->WriteServ("473 %s %s :Cannot join channel (Invite only)",user->nick, Ptr->name);
return NULL;
}
}
user->RemoveInvite(Ptr->name);
}
if (Ptr->limit)
{
MOD_RESULT = 0;
FOREACH_RESULT_I(Instance,I_OnCheckLimit,OnCheckLimit(user, Ptr));
if (!MOD_RESULT)
{
if (Ptr->GetUserCounter() >= Ptr->limit)
{
user->WriteServ("471 %s %s :Cannot join channel (Channel is full)",user->nick, Ptr->name);
return NULL;
}
}
}
if (Ptr->bans.size())
{
if (Ptr->IsBanned(user))
{
user->WriteServ("474 %s %s :Cannot join channel (You're banned)",user->nick, Ptr->name);
return NULL;
}
}
}
}
}
/* NOTE: If the user is an oper here, we can extend their user->chans by up to
* OperMaxchans. For remote users which are not bound by the channel limits,
* we can extend infinitely. Otherwise, nope, youre restricted to MaxChans.
*/
if (!IS_LOCAL(user) || override == true)
{
return chanrec::ForceChan(Instance, Ptr, user, privs);
}
else if (IS_OPER(user))
{
/* Oper allows extension up to the OperMaxchans value */
if (user->chans.size() < Instance->Config->OperMaxChans)
{
return chanrec::ForceChan(Instance, Ptr, user, privs);
}
}
else if (user->chans.size() < Instance->Config->MaxChans)
{
return chanrec::ForceChan(Instance, Ptr, user, privs);
}
user->WriteServ("405 %s %s :You are on too many channels",user->nick, cname);
if (new_channel)
{
/* Things went seriously pear shaped, so take this away. bwahaha. */
chan_hash::iterator n = Instance->chanlist->find(cname);
if (n != Instance->chanlist->end())
{
Ptr->DelUser(user);
DELETE(Ptr);
Instance->chanlist->erase(n);
}
}
return NULL;
}
chanrec* chanrec::ForceChan(InspIRCd* Instance, chanrec* Ptr, userrec* user, const std::string &privs)
{
userrec* dummyuser = new userrec(Instance);
std::string nick = user->nick;
bool silent = false;
dummyuser->SetFd(FD_MAGIC_NUMBER);
Ptr->AddUser(user);
/* Just in case they have no permissions */
user->chans[Ptr] = 0;
for (std::string::const_iterator x = privs.begin(); x != privs.end(); x++)
{
const char status = *x;
ModeHandler* mh = Instance->Modes->FindPrefix(status);
if (mh)
{
Ptr->SetPrefix(user, status, mh->GetPrefixRank(), true);
/* Make sure that the mode handler knows this mode was now set */
mh->OnModeChange(dummyuser, dummyuser, Ptr, nick, true);
switch (mh->GetPrefix())
{
/* These logic ops are SAFE IN THIS CASE
* because if the entry doesnt exist,
* addressing operator[] creates it.
* If they do exist, it points to it.
* At all other times where we dont want
* to create an item if it doesnt exist, we
* must stick to ::find().
*/
case '@':
user->chans[Ptr] |= UCMODE_OP;
break;
case '%':
user->chans[Ptr] |= UCMODE_HOP;
break;
case '+':
user->chans[Ptr] |= UCMODE_VOICE;
break;
}
}
}
delete dummyuser;
FOREACH_MOD_I(Instance,I_OnUserJoin,OnUserJoin(user, Ptr, silent));
if (!silent)
Ptr->WriteChannel(user,"JOIN :%s",Ptr->name);
/* Theyre not the first ones in here, make sure everyone else sees the modes we gave the user */
std::string ms = Instance->Modes->ModeString(user, Ptr);
if ((Ptr->GetUserCounter() > 1) && (ms.length()))
Ptr->WriteAllExceptSender(user, true, 0, "MODE %s +%s", Ptr->name, ms.c_str());
/* Major improvement by Brain - we dont need to be calculating all this pointlessly for remote users */
if (IS_LOCAL(user))
{
if (Ptr->topicset)
{
user->WriteServ("332 %s %s :%s", user->nick, Ptr->name, Ptr->topic);
user->WriteServ("333 %s %s %s %lu", user->nick, Ptr->name, Ptr->setby, (unsigned long)Ptr->topicset);
}
Ptr->UserList(user);
}
FOREACH_MOD_I(Instance,I_OnPostJoin,OnPostJoin(user, Ptr));
return Ptr;
}
bool chanrec::IsBanned(userrec* user)
{
char mask[MAXBUF];
int MOD_RESULT = 0;
FOREACH_RESULT(I_OnCheckBan,OnCheckBan(user, this));
if (!MOD_RESULT)
{
snprintf(mask, MAXBUF, "%s!%s@%s", user->nick, user->ident, user->GetIPString());
for (BanList::iterator i = this->bans.begin(); i != this->bans.end(); i++)
{
/* This allows CIDR ban matching
*
* Full masked host Full unmasked host IP with/without CIDR
*/
if ((match(user->GetFullHost(),i->data)) || (match(user->GetFullRealHost(),i->data)) || (match(mask, i->data, true)))
{
return true;
}
}
}
return false;
}
/* chanrec::PartUser
* remove a channel from a users record, and return the number of users left.
* Therefore, if this function returns 0 the caller should delete the chanrec.
*/
long chanrec::PartUser(userrec *user, const char* reason)
{
bool silent = false;
if (!user)
return this->GetUserCounter();
UCListIter i = user->chans.find(this);
if (i != user->chans.end())
{
FOREACH_MOD(I_OnUserPart,OnUserPart(user, this, reason ? reason : "", silent));
if (!silent)
this->WriteChannel(user, "PART %s%s%s", this->name, reason ? " :" : "", reason ? reason : "");
user->chans.erase(i);
this->RemoveAllPrefixes(user);
}
if (!this->DelUser(user)) /* if there are no users left on the channel... */
{
chan_hash::iterator iter = ServerInstance->chanlist->find(this->name);
/* kill the record */
if (iter != ServerInstance->chanlist->end())
{
FOREACH_MOD(I_OnChannelDelete,OnChannelDelete(this));
ServerInstance->chanlist->erase(iter);
}
return 0;
}
return this->GetUserCounter();
}
long chanrec::ServerKickUser(userrec* user, const char* reason, bool triggerevents)
{
bool silent = false;
if (!user || !reason)
return this->GetUserCounter();
if (IS_LOCAL(user))
{
if (!this->HasUser(user))
{
/* Not on channel */
return this->GetUserCounter();
}
}
if (triggerevents)
{
FOREACH_MOD(I_OnUserKick,OnUserKick(NULL, user, this, reason, silent));
}
UCListIter i = user->chans.find(this);
if (i != user->chans.end())
{
if (!silent)
this->WriteChannelWithServ(ServerInstance->Config->ServerName, "KICK %s %s :%s", this->name, user->nick, reason);
user->chans.erase(i);
this->RemoveAllPrefixes(user);
}
if (!this->DelUser(user))
{
chan_hash::iterator iter = ServerInstance->chanlist->find(this->name);
/* kill the record */
if (iter != ServerInstance->chanlist->end())
{
FOREACH_MOD(I_OnChannelDelete,OnChannelDelete(this));
ServerInstance->chanlist->erase(iter);
}
return 0;
}
return this->GetUserCounter();
}
long chanrec::KickUser(userrec *src, userrec *user, const char* reason)
{
bool silent = false;
if (!src || !user || !reason)
return this->GetUserCounter();
if (IS_LOCAL(src))
{
if (!this->HasUser(user))
{
src->WriteServ("441 %s %s %s :They are not on that channel",src->nick, user->nick, this->name);
return this->GetUserCounter();
}
if ((ServerInstance->ULine(user->server)) && (!ServerInstance->ULine(src->server)))
{
src->WriteServ("482 %s %s :Only a u-line may kick a u-line from a channel.",src->nick, this->name);
return this->GetUserCounter();
}
int MOD_RESULT = 0;
if (!ServerInstance->ULine(src->server))
{
MOD_RESULT = 0;
FOREACH_RESULT(I_OnUserPreKick,OnUserPreKick(src,user,this,reason));
if (MOD_RESULT == 1)
return this->GetUserCounter();
}
/* Set to -1 by OnUserPreKick if explicit allow was set */
if (MOD_RESULT != -1)
{
FOREACH_RESULT(I_OnAccessCheck,OnAccessCheck(src,user,this,AC_KICK));
if ((MOD_RESULT == ACR_DENY) && (!ServerInstance->ULine(src->server)))
return this->GetUserCounter();
if ((MOD_RESULT == ACR_DEFAULT) || (!ServerInstance->ULine(src->server)))
{
int them = this->GetStatus(src);
int us = this->GetStatus(user);
if ((them < STATUS_HOP) || (them < us))
{
src->WriteServ("482 %s %s :You must be a channel %soperator",src->nick, this->name, them == STATUS_HOP ? "" : "half-");
return this->GetUserCounter();
}
}
}
}
FOREACH_MOD(I_OnUserKick,OnUserKick(src, user, this, reason, silent));
UCListIter i = user->chans.find(this);
if (i != user->chans.end())
{
/* zap it from the channel list of the user */
if (!silent)
this->WriteChannel(src, "KICK %s %s :%s", this->name, user->nick, reason);
user->chans.erase(i);
this->RemoveAllPrefixes(user);
}
if (!this->DelUser(user))
/* if there are no users left on the channel */
{
chan_hash::iterator iter = ServerInstance->chanlist->find(this->name);
/* kill the record */
if (iter != ServerInstance->chanlist->end())
{
FOREACH_MOD(I_OnChannelDelete,OnChannelDelete(this));
ServerInstance->chanlist->erase(iter);
}
return 0;
}
return this->GetUserCounter();
}
void chanrec::WriteChannel(userrec* user, char* text, ...)
{
char textbuffer[MAXBUF];
va_list argsPtr;
if (!user || !text)
return;
va_start(argsPtr, text);
vsnprintf(textbuffer, MAXBUF, text, argsPtr);
va_end(argsPtr);
this->WriteChannel(user, std::string(textbuffer));
}
void chanrec::WriteChannel(userrec* user, const std::string &text)
{
CUList *ulist = this->GetUsers();
char tb[MAXBUF];
if (!user)
return;
snprintf(tb,MAXBUF,":%s %s",user->GetFullHost(),text.c_str());
std::string out = tb;
for (CUList::iterator i = ulist->begin(); i != ulist->end(); i++)
{
if (IS_LOCAL(i->first))
i->first->Write(out);
}
}
void chanrec::WriteChannelWithServ(const char* ServName, const char* text, ...)
{
char textbuffer[MAXBUF];
va_list argsPtr;
if (!text)
return;
va_start(argsPtr, text);
vsnprintf(textbuffer, MAXBUF, text, argsPtr);
va_end(argsPtr);
this->WriteChannelWithServ(ServName, std::string(textbuffer));
}
void chanrec::WriteChannelWithServ(const char* ServName, const std::string &text)
{
CUList *ulist = this->GetUsers();
char tb[MAXBUF];
snprintf(tb,MAXBUF,":%s %s",ServName ? ServName : ServerInstance->Config->ServerName, text.c_str());
std::string out = tb;
for (CUList::iterator i = ulist->begin(); i != ulist->end(); i++)
{
if (IS_LOCAL(i->first))
i->first->Write(out);
}
}
/* write formatted text from a source user to all users on a channel except
* for the sender (for privmsg etc) */
void chanrec::WriteAllExceptSender(userrec* user, bool serversource, char status, char* text, ...)
{
char textbuffer[MAXBUF];
va_list argsPtr;
if (!text)
return;
va_start(argsPtr, text);
vsnprintf(textbuffer, MAXBUF, text, argsPtr);
va_end(argsPtr);
this->WriteAllExceptSender(user, serversource, status, std::string(textbuffer));
}
void chanrec::WriteAllExcept(userrec* user, bool serversource, char status, CUList &except_list, char* text, ...)
{
char textbuffer[MAXBUF];
va_list argsPtr;
if (!text)
return;
va_start(argsPtr, text);
vsnprintf(textbuffer, MAXBUF, text, argsPtr);
va_end(argsPtr);
this->WriteAllExcept(user, serversource, status, except_list, std::string(textbuffer));
}
void chanrec::WriteAllExcept(userrec* user, bool serversource, char status, CUList &except_list, const std::string &text)
{
CUList *ulist;
char tb[MAXBUF];
switch (status)
{
case '@':
ulist = this->GetOppedUsers();
break;
case '%':
ulist = this->GetHalfoppedUsers();
break;
case '+':
ulist = this->GetVoicedUsers();
break;
default:
ulist = this->GetUsers();
break;
}
snprintf(tb,MAXBUF,":%s %s",user->GetFullHost(),text.c_str());
std::string out = tb;
for (CUList::iterator i = ulist->begin(); i != ulist->end(); i++)
{
if ((IS_LOCAL(i->first)) && (except_list.find(i->first) == except_list.end()))
{
if (serversource)
i->first->WriteServ(text);
else
i->first->Write(out);
}
}
}
void chanrec::WriteAllExceptSender(userrec* user, bool serversource, char status, const std::string& text)
{
CUList except_list;
except_list[user] = user->nick;
this->WriteAllExcept(user, serversource, status, except_list, std::string(text));
}
/*
* return a count of the users on a specific channel accounting for
* invisible users who won't increase the count. e.g. for /LIST
*/
int chanrec::CountInvisible()
{
int count = 0;
CUList *ulist= this->GetUsers();
for (CUList::iterator i = ulist->begin(); i != ulist->end(); i++)
{
if (!(i->first->IsModeSet('i')))
count++;
}
return count;
}
char* chanrec::ChanModes(bool showkey)
{
static char scratch[MAXBUF];
static char sparam[MAXBUF];
char* offset = scratch;
std::string extparam;
*scratch = '\0';
*sparam = '\0';
/* This was still iterating up to 190, chanrec::modes is only 64 elements -- Om */
for(int n = 0; n < 64; n++)
{
if(this->modes[n])
{
*offset++ = n + 65;
extparam.clear();
switch (n)
{
case CM_KEY:
extparam = (showkey ? this->key : "<key>");
break;
case CM_LIMIT:
extparam = ConvToStr(this->limit);
break;
case CM_NOEXTERNAL:
case CM_TOPICLOCK:
case CM_INVITEONLY:
case CM_MODERATED:
case CM_SECRET:
case CM_PRIVATE:
/* We know these have no parameters */
break;
default:
extparam = this->GetModeParameter(n + 65);
break;
}
if (!extparam.empty())
{
charlcat(sparam,' ',MAXBUF);
strlcat(sparam,extparam.c_str(),MAXBUF);
}
}
}
/* Null terminate scratch */
*offset = '\0';
strlcat(scratch,sparam,MAXBUF);
return scratch;
}
/* compile a userlist of a channel into a string, each nick seperated by
* spaces and op, voice etc status shown as @ and +, and send it to 'user'
*/
void chanrec::UserList(userrec *user, CUList *ulist)
{
char list[MAXBUF];
size_t dlen, curlen;
int MOD_RESULT = 0;
if (!IS_LOCAL(user))
return;
FOREACH_RESULT(I_OnUserList,OnUserList(user, this, ulist));
if (MOD_RESULT == 1)
return;
dlen = curlen = snprintf(list,MAXBUF,"353 %s = %s :", user->nick, this->name);
int numusers = 0;
char* ptr = list + dlen;
if (!ulist)
ulist = this->GetUsers();
/* Improvement by Brain - this doesnt change in value, so why was it inside
* the loop?
*/
bool has_user = this->HasUser(user);
for (CUList::iterator i = ulist->begin(); i != ulist->end(); i++)
{
if ((!has_user) && (i->first->modes[UM_INVISIBLE]))
{
/*
* user is +i, and source not on the channel, does not show
* nick in NAMES list
*/
continue;
}
if (i->first->Visibility && !i->first->Visibility->VisibleTo(user))
continue;
size_t ptrlen = snprintf(ptr, MAXBUF, "%s%s ", this->GetPrefixChar(i->first), i->second.c_str());
/* OnUserList can change this - reset it back to normal */
i->second = i->first->nick;
curlen += ptrlen;
ptr += ptrlen;
numusers++;
if (curlen > (480-NICKMAX))
{
/* list overflowed into multiple numerics */
user->WriteServ(std::string(list));
/* reset our lengths */
dlen = curlen = snprintf(list,MAXBUF,"353 %s = %s :", user->nick, this->name);
ptr = list + dlen;
ptrlen = 0;
numusers = 0;
}
}
/* if whats left in the list isnt empty, send it */
if (numusers)
{
user->WriteServ(std::string(list));
}
user->WriteServ("366 %s %s :End of /NAMES list.", user->nick, this->name);
}
long chanrec::GetMaxBans()
{
/* Return the cached value if there is one */
if (this->maxbans)
return this->maxbans;
/* If there isnt one, we have to do some O(n) hax to find it the first time. (ick) */
for (std::map<std::string,int>::iterator n = ServerInstance->Config->maxbans.begin(); n != ServerInstance->Config->maxbans.end(); n++)
{
if (match(this->name,n->first.c_str()))
{
this->maxbans = n->second;
return n->second;
}
}
/* Screw it, just return the default of 64 */
this->maxbans = 64;
return this->maxbans;
}
void chanrec::ResetMaxBans()
{
this->maxbans = 0;
}
/* returns the status character for a given user on a channel, e.g. @ for op,
* % for halfop etc. If the user has several modes set, the highest mode
* the user has must be returned.
*/
const char* chanrec::GetPrefixChar(userrec *user)
{
static char pf[2] = {0, 0};
prefixlist::iterator n = prefixes.find(user);
if (n != prefixes.end())
{
if (n->second.size())
{
/* If the user has any prefixes, their highest prefix
* will always be at the head of the list, as the list is
* sorted in rank order highest first (see SetPrefix()
* for reasons why)
*/
*pf = n->second.begin()->first;
return pf;
}
}
*pf = 0;
return pf;
}
const char* chanrec::GetAllPrefixChars(userrec* user)
{
static char prefix[MAXBUF];
int ctr = 0;
*prefix = 0;
prefixlist::iterator n = prefixes.find(user);
if (n != prefixes.end())
{
for (std::vector<prefixtype>::iterator x = n->second.begin(); x != n->second.end(); x++)
{
prefix[ctr++] = x->first;
}
}
prefix[ctr] = 0;
return prefix;
}
unsigned int chanrec::GetPrefixValue(userrec* user)
{
prefixlist::iterator n = prefixes.find(user);
if (n != prefixes.end())
{
if (n->second.size())
return n->second.begin()->second;
}
return 0;
}
int chanrec::GetStatusFlags(userrec *user)
{
UCListIter i = user->chans.find(this);
if (i != user->chans.end())
{
return i->second;
}
return 0;
}
int chanrec::GetStatus(userrec *user)
{
if (ServerInstance->ULine(user->server))
return STATUS_OP;
UCListIter i = user->chans.find(this);
if (i != user->chans.end())
{
if ((i->second & UCMODE_OP) > 0)
{
return STATUS_OP;
}
if ((i->second & UCMODE_HOP) > 0)
{
return STATUS_HOP;
}
if ((i->second & UCMODE_VOICE) > 0)
{
return STATUS_VOICE;
}
return STATUS_NORMAL;
}
return STATUS_NORMAL;
}
void chanrec::SetPrefix(userrec* user, char prefix, unsigned int prefix_value, bool adding)
{
prefixlist::iterator n = prefixes.find(user);
prefixtype pfx = std::make_pair(prefix,prefix_value);
if (adding)
{
if (n != prefixes.end())
{
if (std::find(n->second.begin(), n->second.end(), pfx) == n->second.end())
{
n->second.push_back(pfx);
/* We must keep prefixes in rank order, largest first.
* This is for two reasons, firstly because x-chat *ass-u-me's* this
* state, and secondly it turns out to be a benefit to us later.
* See above in GetPrefix().
*/
std::sort(n->second.begin(), n->second.end(), ModeParser::PrefixComparison);
}
}
else
{
pfxcontainer one;
one.push_back(pfx);
prefixes.insert(std::make_pair<userrec*,pfxcontainer>(user, one));
}
}
else
{
if (n != prefixes.end())
{
pfxcontainer::iterator x = std::find(n->second.begin(), n->second.end(), pfx);
if (x != n->second.end())
n->second.erase(x);
}
}
}
void chanrec::RemoveAllPrefixes(userrec* user)
{
prefixlist::iterator n = prefixes.find(user);
if (n != prefixes.end())
{
prefixes.erase(n);
}
}
\ No newline at end of file +/* +------------------------------------+ + * | 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 <stdarg.h> +#include "configreader.h" +#include "users.h" +#include "modules.h" +#include "wildcard.h" +#include "mode.h" + +chanrec::chanrec(InspIRCd* Instance) : ServerInstance(Instance) +{ + *name = *topic = *setby = *key = 0; + maxbans = created = topicset = limit = 0; + memset(&modes,0,64); + age = ServerInstance->Time(true); +} + +void chanrec::SetMode(char mode,bool mode_on) +{ + modes[mode-65] = mode_on; + if (!mode_on) + this->SetModeParam(mode,"",false); +} + + +void chanrec::SetModeParam(char mode,const char* parameter,bool mode_on) +{ + CustomModeList::iterator n = custom_mode_params.find(mode); + + if (mode_on) + { + if (n == custom_mode_params.end()) + custom_mode_params[mode] = strdup(parameter); + } + else + { + if (n != custom_mode_params.end()) + { + free(n->second); + custom_mode_params.erase(n); + } + } +} + +bool chanrec::IsModeSet(char mode) +{ + return modes[mode-65]; +} + +std::string chanrec::GetModeParameter(char mode) +{ + switch (mode) + { + case 'k': + return this->key; + case 'l': + return ConvToStr(this->limit); + default: + CustomModeList::iterator n = custom_mode_params.find(mode); + if (n != custom_mode_params.end()) + return n->second; + return ""; + break; + } +} + +long chanrec::GetUserCounter() +{ + return (this->internal_userlist.size()); +} + +void chanrec::AddUser(userrec* user) +{ + internal_userlist[user] = user->nick; +} + +unsigned long chanrec::DelUser(userrec* user) +{ + CUListIter a = internal_userlist.find(user); + + if (a != internal_userlist.end()) + { + internal_userlist.erase(a); + /* And tidy any others... */ + DelOppedUser(user); + DelHalfoppedUser(user); + DelVoicedUser(user); + } + + return internal_userlist.size(); +} + +bool chanrec::HasUser(userrec* user) +{ + return (internal_userlist.find(user) != internal_userlist.end()); +} + +void chanrec::AddOppedUser(userrec* user) +{ + internal_op_userlist[user] = user->nick; +} + +void chanrec::DelOppedUser(userrec* user) +{ + CUListIter a = internal_op_userlist.find(user); + if (a != internal_op_userlist.end()) + { + internal_op_userlist.erase(a); + return; + } +} + +void chanrec::AddHalfoppedUser(userrec* user) +{ + internal_halfop_userlist[user] = user->nick; +} + +void chanrec::DelHalfoppedUser(userrec* user) +{ + CUListIter a = internal_halfop_userlist.find(user); + + if (a != internal_halfop_userlist.end()) + { + internal_halfop_userlist.erase(a); + } +} + +void chanrec::AddVoicedUser(userrec* user) +{ + internal_voice_userlist[user] = user->nick; +} + +void chanrec::DelVoicedUser(userrec* user) +{ + CUListIter a = internal_voice_userlist.find(user); + + if (a != internal_voice_userlist.end()) + { + internal_voice_userlist.erase(a); + } +} + +CUList* chanrec::GetUsers() +{ + return &internal_userlist; +} + +CUList* chanrec::GetOppedUsers() +{ + return &internal_op_userlist; +} + +CUList* chanrec::GetHalfoppedUsers() +{ + return &internal_halfop_userlist; +} + +CUList* chanrec::GetVoicedUsers() +{ + return &internal_voice_userlist; +} + +void chanrec::SetDefaultModes() +{ + irc::spacesepstream list(ServerInstance->Config->DefaultModes); + std::string modeseq = list.GetToken(); + std::string parameter; + userrec* dummyuser = new userrec(ServerInstance); + dummyuser->SetFd(FD_MAGIC_NUMBER); + + for (std::string::iterator n = modeseq.begin(); n != modeseq.end(); ++n) + { + ModeHandler* mode = ServerInstance->Modes->FindMode(*n, MODETYPE_CHANNEL); + if (mode) + { + if (mode->GetNumParams(true)) + parameter = list.GetToken().c_str(); + else + parameter.clear(); + + mode->OnModeChange(dummyuser, dummyuser, this, parameter, true); + } + } + + delete dummyuser; +} + +/* + * add a channel to a user, creating the record for it if needed and linking + * it to the user record + */ +chanrec* chanrec::JoinUser(InspIRCd* Instance, userrec *user, const char* cn, bool override, const char* key, time_t TS) +{ + if (!user || !cn) + return NULL; + + bool new_channel = false; + char cname[MAXBUF]; + int MOD_RESULT = 0; + strlcpy(cname,cn,CHANMAX); + + std::string privs; + + chanrec* Ptr = Instance->FindChan(cname); + + if (!Ptr) + { + if ((!IS_LOCAL(user)) && (!TS)) + Instance->Log(DEBUG,"*** BUG *** chanrec::JoinUser called for REMOTE user '%s' on channel '%s' but no TS given!", user->nick, cn); + + privs = "@"; + + if (IS_LOCAL(user) && override == false) + { + MOD_RESULT = 0; + FOREACH_RESULT_I(Instance,I_OnUserPreJoin,OnUserPreJoin(user,NULL,cname,privs)); + if (MOD_RESULT == 1) + return NULL; + } + + /* create a new one */ + Ptr = new chanrec(Instance); + (*(Instance->chanlist))[cname] = Ptr; + + strlcpy(Ptr->name, cname,CHANMAX); + + /* As spotted by jilles, dont bother to set this on remote users */ + if (IS_LOCAL(user)) + Ptr->SetDefaultModes(); + + Ptr->created = TS ? TS : Instance->Time(); + Ptr->age = Ptr->created; + *Ptr->topic = 0; + *Ptr->setby = 0; + Ptr->topicset = 0; + new_channel = true; + } + else + { + /* Already on the channel */ + if (Ptr->HasUser(user)) + return NULL; + + /* + * remote users are allowed us to bypass channel modes + * and bans (used by servers) + */ + if (IS_LOCAL(user) && override == false) + { + MOD_RESULT = 0; + FOREACH_RESULT_I(Instance,I_OnUserPreJoin,OnUserPreJoin(user,Ptr,cname,privs)); + if (MOD_RESULT == 1) + { + return NULL; + } + else if (MOD_RESULT == 0) + { + if (*Ptr->key) + { + MOD_RESULT = 0; + FOREACH_RESULT_I(Instance,I_OnCheckKey,OnCheckKey(user, Ptr, key ? key : "")); + if (!MOD_RESULT) + { + if ((!key) || strcmp(key,Ptr->key)) + { + user->WriteServ("475 %s %s :Cannot join channel (Incorrect channel key)",user->nick, Ptr->name); + return NULL; + } + } + } + if (Ptr->modes[CM_INVITEONLY]) + { + MOD_RESULT = 0; + FOREACH_RESULT_I(Instance,I_OnCheckInvite,OnCheckInvite(user, Ptr)); + if (!MOD_RESULT) + { + if (user->IsInvited(Ptr->name)) + { + /* user was invited to channel */ + /* there may be an optional channel NOTICE here */ + } + else + { + user->WriteServ("473 %s %s :Cannot join channel (Invite only)",user->nick, Ptr->name); + return NULL; + } + } + user->RemoveInvite(Ptr->name); + } + if (Ptr->limit) + { + MOD_RESULT = 0; + FOREACH_RESULT_I(Instance,I_OnCheckLimit,OnCheckLimit(user, Ptr)); + if (!MOD_RESULT) + { + if (Ptr->GetUserCounter() >= Ptr->limit) + { + user->WriteServ("471 %s %s :Cannot join channel (Channel is full)",user->nick, Ptr->name); + return NULL; + } + } + } + if (Ptr->bans.size()) + { + if (Ptr->IsBanned(user)) + { + user->WriteServ("474 %s %s :Cannot join channel (You're banned)",user->nick, Ptr->name); + return NULL; + } + } + } + } + } + + /* NOTE: If the user is an oper here, we can extend their user->chans by up to + * OperMaxchans. For remote users which are not bound by the channel limits, + * we can extend infinitely. Otherwise, nope, youre restricted to MaxChans. + */ + if (!IS_LOCAL(user) || override == true) + { + return chanrec::ForceChan(Instance, Ptr, user, privs); + } + else if (IS_OPER(user)) + { + /* Oper allows extension up to the OperMaxchans value */ + if (user->chans.size() < Instance->Config->OperMaxChans) + { + return chanrec::ForceChan(Instance, Ptr, user, privs); + } + } + else if (user->chans.size() < Instance->Config->MaxChans) + { + return chanrec::ForceChan(Instance, Ptr, user, privs); + } + + + user->WriteServ("405 %s %s :You are on too many channels",user->nick, cname); + + if (new_channel) + { + /* Things went seriously pear shaped, so take this away. bwahaha. */ + chan_hash::iterator n = Instance->chanlist->find(cname); + if (n != Instance->chanlist->end()) + { + Ptr->DelUser(user); + DELETE(Ptr); + Instance->chanlist->erase(n); + } + } + + return NULL; +} + +chanrec* chanrec::ForceChan(InspIRCd* Instance, chanrec* Ptr, userrec* user, const std::string &privs) +{ + userrec* dummyuser = new userrec(Instance); + std::string nick = user->nick; + bool silent = false; + + dummyuser->SetFd(FD_MAGIC_NUMBER); + Ptr->AddUser(user); + + /* Just in case they have no permissions */ + user->chans[Ptr] = 0; + + for (std::string::const_iterator x = privs.begin(); x != privs.end(); x++) + { + const char status = *x; + ModeHandler* mh = Instance->Modes->FindPrefix(status); + if (mh) + { + Ptr->SetPrefix(user, status, mh->GetPrefixRank(), true); + /* Make sure that the mode handler knows this mode was now set */ + mh->OnModeChange(dummyuser, dummyuser, Ptr, nick, true); + + switch (mh->GetPrefix()) + { + /* These logic ops are SAFE IN THIS CASE + * because if the entry doesnt exist, + * addressing operator[] creates it. + * If they do exist, it points to it. + * At all other times where we dont want + * to create an item if it doesnt exist, we + * must stick to ::find(). + */ + case '@': + user->chans[Ptr] |= UCMODE_OP; + break; + case '%': + user->chans[Ptr] |= UCMODE_HOP; + break; + case '+': + user->chans[Ptr] |= UCMODE_VOICE; + break; + } + } + } + + delete dummyuser; + + FOREACH_MOD_I(Instance,I_OnUserJoin,OnUserJoin(user, Ptr, silent)); + + if (!silent) + Ptr->WriteChannel(user,"JOIN :%s",Ptr->name); + + /* Theyre not the first ones in here, make sure everyone else sees the modes we gave the user */ + std::string ms = Instance->Modes->ModeString(user, Ptr); + if ((Ptr->GetUserCounter() > 1) && (ms.length())) + Ptr->WriteAllExceptSender(user, true, 0, "MODE %s +%s", Ptr->name, ms.c_str()); + + /* Major improvement by Brain - we dont need to be calculating all this pointlessly for remote users */ + if (IS_LOCAL(user)) + { + if (Ptr->topicset) + { + user->WriteServ("332 %s %s :%s", user->nick, Ptr->name, Ptr->topic); + user->WriteServ("333 %s %s %s %lu", user->nick, Ptr->name, Ptr->setby, (unsigned long)Ptr->topicset); + } + Ptr->UserList(user); + } + FOREACH_MOD_I(Instance,I_OnPostJoin,OnPostJoin(user, Ptr)); + return Ptr; +} + +bool chanrec::IsBanned(userrec* user) +{ + char mask[MAXBUF]; + int MOD_RESULT = 0; + FOREACH_RESULT(I_OnCheckBan,OnCheckBan(user, this)); + if (!MOD_RESULT) + { + snprintf(mask, MAXBUF, "%s!%s@%s", user->nick, user->ident, user->GetIPString()); + for (BanList::iterator i = this->bans.begin(); i != this->bans.end(); i++) + { + /* This allows CIDR ban matching + * + * Full masked host Full unmasked host IP with/without CIDR + */ + if ((match(user->GetFullHost(),i->data)) || (match(user->GetFullRealHost(),i->data)) || (match(mask, i->data, true))) + { + return true; + } + } + } + return false; +} + +/* chanrec::PartUser + * remove a channel from a users record, and return the number of users left. + * Therefore, if this function returns 0 the caller should delete the chanrec. + */ +long chanrec::PartUser(userrec *user, const char* reason) +{ + bool silent = false; + + if (!user) + return this->GetUserCounter(); + + UCListIter i = user->chans.find(this); + if (i != user->chans.end()) + { + FOREACH_MOD(I_OnUserPart,OnUserPart(user, this, reason ? reason : "", silent)); + + if (!silent) + this->WriteChannel(user, "PART %s%s%s", this->name, reason ? " :" : "", reason ? reason : ""); + + user->chans.erase(i); + this->RemoveAllPrefixes(user); + } + + if (!this->DelUser(user)) /* if there are no users left on the channel... */ + { + chan_hash::iterator iter = ServerInstance->chanlist->find(this->name); + /* kill the record */ + if (iter != ServerInstance->chanlist->end()) + { + FOREACH_MOD(I_OnChannelDelete,OnChannelDelete(this)); + ServerInstance->chanlist->erase(iter); + } + return 0; + } + + return this->GetUserCounter(); +} + +long chanrec::ServerKickUser(userrec* user, const char* reason, bool triggerevents) +{ + bool silent = false; + + if (!user || !reason) + return this->GetUserCounter(); + + if (IS_LOCAL(user)) + { + if (!this->HasUser(user)) + { + /* Not on channel */ + return this->GetUserCounter(); + } + } + + if (triggerevents) + { + FOREACH_MOD(I_OnUserKick,OnUserKick(NULL, user, this, reason, silent)); + } + + UCListIter i = user->chans.find(this); + if (i != user->chans.end()) + { + if (!silent) + this->WriteChannelWithServ(ServerInstance->Config->ServerName, "KICK %s %s :%s", this->name, user->nick, reason); + + user->chans.erase(i); + this->RemoveAllPrefixes(user); + } + + if (!this->DelUser(user)) + { + chan_hash::iterator iter = ServerInstance->chanlist->find(this->name); + /* kill the record */ + if (iter != ServerInstance->chanlist->end()) + { + FOREACH_MOD(I_OnChannelDelete,OnChannelDelete(this)); + ServerInstance->chanlist->erase(iter); + } + return 0; + } + + return this->GetUserCounter(); +} + +long chanrec::KickUser(userrec *src, userrec *user, const char* reason) +{ + bool silent = false; + + if (!src || !user || !reason) + return this->GetUserCounter(); + + if (IS_LOCAL(src)) + { + if (!this->HasUser(user)) + { + src->WriteServ("441 %s %s %s :They are not on that channel",src->nick, user->nick, this->name); + return this->GetUserCounter(); + } + if ((ServerInstance->ULine(user->server)) && (!ServerInstance->ULine(src->server))) + { + src->WriteServ("482 %s %s :Only a u-line may kick a u-line from a channel.",src->nick, this->name); + return this->GetUserCounter(); + } + int MOD_RESULT = 0; + + if (!ServerInstance->ULine(src->server)) + { + MOD_RESULT = 0; + FOREACH_RESULT(I_OnUserPreKick,OnUserPreKick(src,user,this,reason)); + if (MOD_RESULT == 1) + return this->GetUserCounter(); + } + /* Set to -1 by OnUserPreKick if explicit allow was set */ + if (MOD_RESULT != -1) + { + FOREACH_RESULT(I_OnAccessCheck,OnAccessCheck(src,user,this,AC_KICK)); + if ((MOD_RESULT == ACR_DENY) && (!ServerInstance->ULine(src->server))) + return this->GetUserCounter(); + + if ((MOD_RESULT == ACR_DEFAULT) || (!ServerInstance->ULine(src->server))) + { + int them = this->GetStatus(src); + int us = this->GetStatus(user); + if ((them < STATUS_HOP) || (them < us)) + { + src->WriteServ("482 %s %s :You must be a channel %soperator",src->nick, this->name, them == STATUS_HOP ? "" : "half-"); + return this->GetUserCounter(); + } + } + } + } + + FOREACH_MOD(I_OnUserKick,OnUserKick(src, user, this, reason, silent)); + + UCListIter i = user->chans.find(this); + if (i != user->chans.end()) + { + /* zap it from the channel list of the user */ + if (!silent) + this->WriteChannel(src, "KICK %s %s :%s", this->name, user->nick, reason); + + user->chans.erase(i); + this->RemoveAllPrefixes(user); + } + + if (!this->DelUser(user)) + /* if there are no users left on the channel */ + { + chan_hash::iterator iter = ServerInstance->chanlist->find(this->name); + + /* kill the record */ + if (iter != ServerInstance->chanlist->end()) + { + FOREACH_MOD(I_OnChannelDelete,OnChannelDelete(this)); + ServerInstance->chanlist->erase(iter); + } + return 0; + } + + return this->GetUserCounter(); +} + +void chanrec::WriteChannel(userrec* user, char* text, ...) +{ + char textbuffer[MAXBUF]; + va_list argsPtr; + + if (!user || !text) + return; + + va_start(argsPtr, text); + vsnprintf(textbuffer, MAXBUF, text, argsPtr); + va_end(argsPtr); + + this->WriteChannel(user, std::string(textbuffer)); +} + +void chanrec::WriteChannel(userrec* user, const std::string &text) +{ + CUList *ulist = this->GetUsers(); + char tb[MAXBUF]; + + if (!user) + return; + + snprintf(tb,MAXBUF,":%s %s",user->GetFullHost(),text.c_str()); + std::string out = tb; + + for (CUList::iterator i = ulist->begin(); i != ulist->end(); i++) + { + if (IS_LOCAL(i->first)) + i->first->Write(out); + } +} + +void chanrec::WriteChannelWithServ(const char* ServName, const char* text, ...) +{ + char textbuffer[MAXBUF]; + va_list argsPtr; + + if (!text) + return; + + va_start(argsPtr, text); + vsnprintf(textbuffer, MAXBUF, text, argsPtr); + va_end(argsPtr); + + this->WriteChannelWithServ(ServName, std::string(textbuffer)); +} + +void chanrec::WriteChannelWithServ(const char* ServName, const std::string &text) +{ + CUList *ulist = this->GetUsers(); + char tb[MAXBUF]; + + snprintf(tb,MAXBUF,":%s %s",ServName ? ServName : ServerInstance->Config->ServerName, text.c_str()); + std::string out = tb; + + for (CUList::iterator i = ulist->begin(); i != ulist->end(); i++) + { + if (IS_LOCAL(i->first)) + i->first->Write(out); + } +} + +/* write formatted text from a source user to all users on a channel except + * for the sender (for privmsg etc) */ +void chanrec::WriteAllExceptSender(userrec* user, bool serversource, char status, char* text, ...) +{ + char textbuffer[MAXBUF]; + va_list argsPtr; + + if (!text) + return; + + va_start(argsPtr, text); + vsnprintf(textbuffer, MAXBUF, text, argsPtr); + va_end(argsPtr); + + this->WriteAllExceptSender(user, serversource, status, std::string(textbuffer)); +} + +void chanrec::WriteAllExcept(userrec* user, bool serversource, char status, CUList &except_list, char* text, ...) +{ + char textbuffer[MAXBUF]; + va_list argsPtr; + + if (!text) + return; + + va_start(argsPtr, text); + vsnprintf(textbuffer, MAXBUF, text, argsPtr); + va_end(argsPtr); + + this->WriteAllExcept(user, serversource, status, except_list, std::string(textbuffer)); +} + +void chanrec::WriteAllExcept(userrec* user, bool serversource, char status, CUList &except_list, const std::string &text) +{ + CUList *ulist; + char tb[MAXBUF]; + + switch (status) + { + case '@': + ulist = this->GetOppedUsers(); + break; + case '%': + ulist = this->GetHalfoppedUsers(); + break; + case '+': + ulist = this->GetVoicedUsers(); + break; + default: + ulist = this->GetUsers(); + break; + } + + snprintf(tb,MAXBUF,":%s %s",user->GetFullHost(),text.c_str()); + std::string out = tb; + + for (CUList::iterator i = ulist->begin(); i != ulist->end(); i++) + { + if ((IS_LOCAL(i->first)) && (except_list.find(i->first) == except_list.end())) + { + if (serversource) + i->first->WriteServ(text); + else + i->first->Write(out); + } + } +} + +void chanrec::WriteAllExceptSender(userrec* user, bool serversource, char status, const std::string& text) +{ + CUList except_list; + except_list[user] = user->nick; + this->WriteAllExcept(user, serversource, status, except_list, std::string(text)); +} + +/* + * return a count of the users on a specific channel accounting for + * invisible users who won't increase the count. e.g. for /LIST + */ +int chanrec::CountInvisible() +{ + int count = 0; + CUList *ulist= this->GetUsers(); + for (CUList::iterator i = ulist->begin(); i != ulist->end(); i++) + { + if (!(i->first->IsModeSet('i'))) + count++; + } + + return count; +} + +char* chanrec::ChanModes(bool showkey) +{ + static char scratch[MAXBUF]; + static char sparam[MAXBUF]; + char* offset = scratch; + std::string extparam; + + *scratch = '\0'; + *sparam = '\0'; + + /* This was still iterating up to 190, chanrec::modes is only 64 elements -- Om */ + for(int n = 0; n < 64; n++) + { + if(this->modes[n]) + { + *offset++ = n + 65; + extparam.clear(); + switch (n) + { + case CM_KEY: + extparam = (showkey ? this->key : "<key>"); + break; + case CM_LIMIT: + extparam = ConvToStr(this->limit); + break; + case CM_NOEXTERNAL: + case CM_TOPICLOCK: + case CM_INVITEONLY: + case CM_MODERATED: + case CM_SECRET: + case CM_PRIVATE: + /* We know these have no parameters */ + break; + default: + extparam = this->GetModeParameter(n + 65); + break; + } + if (!extparam.empty()) + { + charlcat(sparam,' ',MAXBUF); + strlcat(sparam,extparam.c_str(),MAXBUF); + } + } + } + + /* Null terminate scratch */ + *offset = '\0'; + strlcat(scratch,sparam,MAXBUF); + return scratch; +} + +/* compile a userlist of a channel into a string, each nick seperated by + * spaces and op, voice etc status shown as @ and +, and send it to 'user' + */ +void chanrec::UserList(userrec *user, CUList *ulist) +{ + char list[MAXBUF]; + size_t dlen, curlen; + int MOD_RESULT = 0; + + if (!IS_LOCAL(user)) + return; + + FOREACH_RESULT(I_OnUserList,OnUserList(user, this, ulist)); + if (MOD_RESULT == 1) + return; + + dlen = curlen = snprintf(list,MAXBUF,"353 %s = %s :", user->nick, this->name); + + int numusers = 0; + char* ptr = list + dlen; + + if (!ulist) + ulist = this->GetUsers(); + + /* Improvement by Brain - this doesnt change in value, so why was it inside + * the loop? + */ + bool has_user = this->HasUser(user); + + for (CUList::iterator i = ulist->begin(); i != ulist->end(); i++) + { + if ((!has_user) && (i->first->modes[UM_INVISIBLE])) + { + /* + * user is +i, and source not on the channel, does not show + * nick in NAMES list + */ + continue; + } + + if (i->first->Visibility && !i->first->Visibility->VisibleTo(user)) + continue; + + size_t ptrlen = snprintf(ptr, MAXBUF, "%s%s ", this->GetPrefixChar(i->first), i->second.c_str()); + /* OnUserList can change this - reset it back to normal */ + i->second = i->first->nick; + + curlen += ptrlen; + ptr += ptrlen; + + numusers++; + + if (curlen > (480-NICKMAX)) + { + /* list overflowed into multiple numerics */ + user->WriteServ(std::string(list)); + + /* reset our lengths */ + dlen = curlen = snprintf(list,MAXBUF,"353 %s = %s :", user->nick, this->name); + ptr = list + dlen; + + ptrlen = 0; + numusers = 0; + } + } + + /* if whats left in the list isnt empty, send it */ + if (numusers) + { + user->WriteServ(std::string(list)); + } + + user->WriteServ("366 %s %s :End of /NAMES list.", user->nick, this->name); +} + +long chanrec::GetMaxBans() +{ + /* Return the cached value if there is one */ + if (this->maxbans) + return this->maxbans; + + /* If there isnt one, we have to do some O(n) hax to find it the first time. (ick) */ + for (std::map<std::string,int>::iterator n = ServerInstance->Config->maxbans.begin(); n != ServerInstance->Config->maxbans.end(); n++) + { + if (match(this->name,n->first.c_str())) + { + this->maxbans = n->second; + return n->second; + } + } + + /* Screw it, just return the default of 64 */ + this->maxbans = 64; + return this->maxbans; +} + +void chanrec::ResetMaxBans() +{ + this->maxbans = 0; +} + +/* returns the status character for a given user on a channel, e.g. @ for op, + * % for halfop etc. If the user has several modes set, the highest mode + * the user has must be returned. + */ +const char* chanrec::GetPrefixChar(userrec *user) +{ + static char pf[2] = {0, 0}; + + prefixlist::iterator n = prefixes.find(user); + if (n != prefixes.end()) + { + if (n->second.size()) + { + /* If the user has any prefixes, their highest prefix + * will always be at the head of the list, as the list is + * sorted in rank order highest first (see SetPrefix() + * for reasons why) + */ + *pf = n->second.begin()->first; + return pf; + } + } + + *pf = 0; + return pf; +} + +const char* chanrec::GetAllPrefixChars(userrec* user) +{ + static char prefix[MAXBUF]; + int ctr = 0; + *prefix = 0; + + prefixlist::iterator n = prefixes.find(user); + if (n != prefixes.end()) + { + for (std::vector<prefixtype>::iterator x = n->second.begin(); x != n->second.end(); x++) + { + prefix[ctr++] = x->first; + } + } + + prefix[ctr] = 0; + + return prefix; +} + +unsigned int chanrec::GetPrefixValue(userrec* user) +{ + prefixlist::iterator n = prefixes.find(user); + if (n != prefixes.end()) + { + if (n->second.size()) + return n->second.begin()->second; + } + return 0; +} + +int chanrec::GetStatusFlags(userrec *user) +{ + UCListIter i = user->chans.find(this); + if (i != user->chans.end()) + { + return i->second; + } + return 0; +} + +int chanrec::GetStatus(userrec *user) +{ + if (ServerInstance->ULine(user->server)) + return STATUS_OP; + + UCListIter i = user->chans.find(this); + if (i != user->chans.end()) + { + if ((i->second & UCMODE_OP) > 0) + { + return STATUS_OP; + } + if ((i->second & UCMODE_HOP) > 0) + { + return STATUS_HOP; + } + if ((i->second & UCMODE_VOICE) > 0) + { + return STATUS_VOICE; + } + return STATUS_NORMAL; + } + return STATUS_NORMAL; +} + +void chanrec::SetPrefix(userrec* user, char prefix, unsigned int prefix_value, bool adding) +{ + prefixlist::iterator n = prefixes.find(user); + prefixtype pfx = std::make_pair(prefix,prefix_value); + if (adding) + { + if (n != prefixes.end()) + { + if (std::find(n->second.begin(), n->second.end(), pfx) == n->second.end()) + { + n->second.push_back(pfx); + /* We must keep prefixes in rank order, largest first. + * This is for two reasons, firstly because x-chat *ass-u-me's* this + * state, and secondly it turns out to be a benefit to us later. + * See above in GetPrefix(). + */ + std::sort(n->second.begin(), n->second.end(), ModeParser::PrefixComparison); + } + } + else + { + pfxcontainer one; + one.push_back(pfx); + prefixes.insert(std::make_pair<userrec*,pfxcontainer>(user, one)); + } + } + else + { + if (n != prefixes.end()) + { + pfxcontainer::iterator x = std::find(n->second.begin(), n->second.end(), pfx); + if (x != n->second.end()) + n->second.erase(x); + } + } +} + +void chanrec::RemoveAllPrefixes(userrec* user) +{ + prefixlist::iterator n = prefixes.find(user); + if (n != prefixes.end()) + { + prefixes.erase(n); + } +} + diff --git a/src/cmd_admin.cpp b/src/cmd_admin.cpp index acfb8da43..698468d0c 100644 --- a/src/cmd_admin.cpp +++ b/src/cmd_admin.cpp @@ -1 +1,35 @@ -/* +------------------------------------+
* | 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 "users.h"
#include "commands/cmd_admin.h"
extern "C" DllExport command_t* init_command(InspIRCd* Instance)
{
return new cmd_admin(Instance);
}
/** Handle /ADMIN
*/
CmdResult cmd_admin::Handle (const char** parameters, int pcnt, userrec *user)
{
user->WriteServ("256 %s :Administrative info for %s",user->nick,ServerInstance->Config->ServerName);
if (*ServerInstance->Config->AdminName)
user->WriteServ("257 %s :Name - %s",user->nick,ServerInstance->Config->AdminName);
user->WriteServ("258 %s :Nickname - %s",user->nick,ServerInstance->Config->AdminNick);
user->WriteServ("258 %s :E-Mail - %s",user->nick,ServerInstance->Config->AdminEmail);
return CMD_SUCCESS;
}
\ No newline at end of file +/* +------------------------------------+ + * | 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 "users.h" +#include "commands/cmd_admin.h" + + +extern "C" DllExport command_t* init_command(InspIRCd* Instance) +{ + return new cmd_admin(Instance); +} + +/** Handle /ADMIN + */ +CmdResult cmd_admin::Handle (const char** parameters, int pcnt, userrec *user) +{ + user->WriteServ("256 %s :Administrative info for %s",user->nick,ServerInstance->Config->ServerName); + if (*ServerInstance->Config->AdminName) + user->WriteServ("257 %s :Name - %s",user->nick,ServerInstance->Config->AdminName); + user->WriteServ("258 %s :Nickname - %s",user->nick,ServerInstance->Config->AdminNick); + user->WriteServ("258 %s :E-Mail - %s",user->nick,ServerInstance->Config->AdminEmail); + return CMD_SUCCESS; +} diff --git a/src/cmd_away.cpp b/src/cmd_away.cpp index 894924938..10f96c80b 100644 --- a/src/cmd_away.cpp +++ b/src/cmd_away.cpp @@ -1 +1,42 @@ -/* +------------------------------------+
* | 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 "users.h"
#include "modules.h"
#include "commands/cmd_away.h"
extern "C" DllExport command_t* init_command(InspIRCd* Instance)
{
return new cmd_away(Instance);
}
/** Handle /AWAY
*/
CmdResult cmd_away::Handle (const char** parameters, int pcnt, userrec *user)
{
if ((pcnt) && (*parameters[0]))
{
strlcpy(user->awaymsg,parameters[0],MAXAWAY);
user->WriteServ("306 %s :You have been marked as being away",user->nick);
FOREACH_MOD(I_OnSetAway,OnSetAway(user));
}
else
{
*user->awaymsg = 0;
user->WriteServ("305 %s :You are no longer marked as being away",user->nick);
FOREACH_MOD(I_OnCancelAway,OnCancelAway(user));
}
return CMD_SUCCESS;
}
\ No newline at end of file +/* +------------------------------------+ + * | 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 "users.h" +#include "modules.h" +#include "commands/cmd_away.h" + +extern "C" DllExport command_t* init_command(InspIRCd* Instance) +{ + return new cmd_away(Instance); +} + +/** Handle /AWAY + */ +CmdResult cmd_away::Handle (const char** parameters, int pcnt, userrec *user) +{ + if ((pcnt) && (*parameters[0])) + { + strlcpy(user->awaymsg,parameters[0],MAXAWAY); + user->WriteServ("306 %s :You have been marked as being away",user->nick); + FOREACH_MOD(I_OnSetAway,OnSetAway(user)); + } + else + { + *user->awaymsg = 0; + user->WriteServ("305 %s :You are no longer marked as being away",user->nick); + FOREACH_MOD(I_OnCancelAway,OnCancelAway(user)); + } + return CMD_SUCCESS; +} diff --git a/src/cmd_clearcache.cpp b/src/cmd_clearcache.cpp index cdc4b987c..daf8fa78b 100644 --- a/src/cmd_clearcache.cpp +++ b/src/cmd_clearcache.cpp @@ -1 +1,31 @@ -/* +------------------------------------+
* | 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 "users.h"
#include "commands/cmd_clearcache.h"
extern "C" DllExport command_t* init_command(InspIRCd* Instance)
{
return new cmd_clearcache(Instance);
}
/** Handle /CLEARCACHE
*/
CmdResult cmd_clearcache::Handle (const char** parameters, int pcnt, userrec *user)
{
int n = ServerInstance->Res->ClearCache();
user->WriteServ("NOTICE %s :*** Cleared DNS cache of %d items.", user->nick, n);
return CMD_SUCCESS;
}
\ No newline at end of file +/* +------------------------------------+ + * | 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 "users.h" +#include "commands/cmd_clearcache.h" + +extern "C" DllExport command_t* init_command(InspIRCd* Instance) +{ + return new cmd_clearcache(Instance); +} + +/** Handle /CLEARCACHE + */ +CmdResult cmd_clearcache::Handle (const char** parameters, int pcnt, userrec *user) +{ + int n = ServerInstance->Res->ClearCache(); + user->WriteServ("NOTICE %s :*** Cleared DNS cache of %d items.", user->nick, n); + return CMD_SUCCESS; +} diff --git a/src/cmd_commands.cpp b/src/cmd_commands.cpp index 9eff6ed50..fc95de5f1 100644 --- a/src/cmd_commands.cpp +++ b/src/cmd_commands.cpp @@ -1 +1,33 @@ -/* +------------------------------------+
* | 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 "users.h"
#include "commands/cmd_commands.h"
/** Handle /COMMANDS
*/
extern "C" DllExport command_t* init_command(InspIRCd* Instance)
{
return new cmd_commands(Instance);
}
CmdResult cmd_commands::Handle (const char** parameters, int pcnt, userrec *user)
{
for (command_table::iterator i = ServerInstance->Parser->cmdlist.begin(); i != ServerInstance->Parser->cmdlist.end(); i++)
{
user->WriteServ("902 %s :%s %s %d",user->nick,i->second->command.c_str(),i->second->source.c_str(),i->second->min_params);
}
user->WriteServ("903 %s :End of COMMANDS list",user->nick);
return CMD_SUCCESS;
}
\ No newline at end of file +/* +------------------------------------+ + * | 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 "users.h" +#include "commands/cmd_commands.h" + +/** Handle /COMMANDS + */ +extern "C" DllExport command_t* init_command(InspIRCd* Instance) +{ + return new cmd_commands(Instance); +} + +CmdResult cmd_commands::Handle (const char** parameters, int pcnt, userrec *user) +{ + for (command_table::iterator i = ServerInstance->Parser->cmdlist.begin(); i != ServerInstance->Parser->cmdlist.end(); i++) + { + user->WriteServ("902 %s :%s %s %d",user->nick,i->second->command.c_str(),i->second->source.c_str(),i->second->min_params); + } + user->WriteServ("903 %s :End of COMMANDS list",user->nick); + return CMD_SUCCESS; +} diff --git a/src/cmd_connect.cpp b/src/cmd_connect.cpp index da4a11565..fc3ae9d7a 100644 --- a/src/cmd_connect.cpp +++ b/src/cmd_connect.cpp @@ -1 +1,33 @@ -/* +------------------------------------+
* | 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 "users.h"
#include "commands/cmd_connect.h"
/*
* This is handled by the server linking module, if necessary. Do not remove this stub.
*/
extern "C" DllExport command_t* init_command(InspIRCd* Instance)
{
return new cmd_connect(Instance);
}
/** Handle /CONNECT
*/
CmdResult cmd_connect::Handle (const char** parameters, int pcnt, userrec *user)
{
user->WriteServ( "NOTICE %s :You are a nub. Load a linking module.", user->nick);
return CMD_SUCCESS;
}
\ No newline at end of file +/* +------------------------------------+ + * | 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 "users.h" +#include "commands/cmd_connect.h" + +/* + * This is handled by the server linking module, if necessary. Do not remove this stub. + */ + +extern "C" DllExport command_t* init_command(InspIRCd* Instance) +{ + return new cmd_connect(Instance); +} + +/** Handle /CONNECT + */ +CmdResult cmd_connect::Handle (const char** parameters, int pcnt, userrec *user) +{ + user->WriteServ( "NOTICE %s :You are a nub. Load a linking module.", user->nick); + return CMD_SUCCESS; +} diff --git a/src/cmd_die.cpp b/src/cmd_die.cpp index c8195a41c..7f0a81df8 100644 --- a/src/cmd_die.cpp +++ b/src/cmd_die.cpp @@ -1 +1,47 @@ -/* +------------------------------------+
* | 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 "users.h"
#include "commands/cmd_die.h"
#include "exitcodes.h"
extern "C" DllExport command_t* init_command(InspIRCd* Instance)
{
return new cmd_die(Instance);
}
/** Handle /DIE
*/
CmdResult cmd_die::Handle (const char** parameters, int pcnt, userrec *user)
{
if (!strcmp(parameters[0],ServerInstance->Config->diepass))
{
std::string diebuf = std::string("*** DIE command from ") + user->nick + "!" + user->ident + "@" + user->dhost + ". Terminating in " + ConvToStr(ServerInstance->Config->DieDelay) + " seconds.";
ServerInstance->Log(SPARSE, diebuf);
ServerInstance->SendError(diebuf);
if (ServerInstance->Config->DieDelay)
sleep(ServerInstance->Config->DieDelay);
InspIRCd::Exit(EXIT_STATUS_DIE);
}
else
{
ServerInstance->Log(SPARSE, "Failed /DIE command from %s!%s@%s", user->nick, user->ident, user->host);
ServerInstance->WriteOpers("*** Failed DIE Command from %s!%s@%s.",user->nick,user->ident,user->host);
return CMD_FAILURE;
}
return CMD_SUCCESS;
}
\ No newline at end of file +/* +------------------------------------+ + * | 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 "users.h" +#include "commands/cmd_die.h" +#include "exitcodes.h" + +extern "C" DllExport command_t* init_command(InspIRCd* Instance) +{ + return new cmd_die(Instance); +} + +/** Handle /DIE + */ +CmdResult cmd_die::Handle (const char** parameters, int pcnt, userrec *user) +{ + if (!strcmp(parameters[0],ServerInstance->Config->diepass)) + { + std::string diebuf = std::string("*** DIE command from ") + user->nick + "!" + user->ident + "@" + user->dhost + ". Terminating in " + ConvToStr(ServerInstance->Config->DieDelay) + " seconds."; + ServerInstance->Log(SPARSE, diebuf); + ServerInstance->SendError(diebuf); + + if (ServerInstance->Config->DieDelay) + sleep(ServerInstance->Config->DieDelay); + + InspIRCd::Exit(EXIT_STATUS_DIE); + } + else + { + ServerInstance->Log(SPARSE, "Failed /DIE command from %s!%s@%s", user->nick, user->ident, user->host); + ServerInstance->WriteOpers("*** Failed DIE Command from %s!%s@%s.",user->nick,user->ident,user->host); + return CMD_FAILURE; + } + return CMD_SUCCESS; +} diff --git a/src/cmd_eline.cpp b/src/cmd_eline.cpp index 0282369c1..793f7a413 100644 --- a/src/cmd_eline.cpp +++ b/src/cmd_eline.cpp @@ -1 +1,77 @@ -/* +------------------------------------+
* | 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 "users.h"
#include "modules.h"
#include "xline.h"
#include "commands/cmd_eline.h"
extern "C" DllExport command_t* init_command(InspIRCd* Instance)
{
return new cmd_eline(Instance);
}
/** Handle /ELINE
*/
CmdResult cmd_eline::Handle (const char** parameters, int pcnt, userrec *user)
{
if (pcnt >= 3)
{
IdentHostPair ih = ServerInstance->XLines->IdentSplit(parameters[0]);
if (ServerInstance->HostMatchesEveryone(ih.first+"@"+ih.second,user))
return CMD_FAILURE;
if (!strchr(parameters[0],'@'))
{
user->WriteServ("NOTICE %s :*** E-Line must contain a username, e.g. *@%s",user->nick,parameters[0]);
return CMD_FAILURE;
}
long duration = ServerInstance->Duration(parameters[1]);
if (ServerInstance->XLines->add_eline(duration,user->nick,parameters[2],parameters[0]))
{
FOREACH_MOD(I_OnAddELine,OnAddELine(duration, user, parameters[2], parameters[0]));
if (!duration)
{
ServerInstance->SNO->WriteToSnoMask('x',"%s added permanent E-line for %s.",user->nick,parameters[0]);
}
else
{
time_t c_requires_crap = duration + ServerInstance->Time();
ServerInstance->SNO->WriteToSnoMask('x',"%s added timed E-line for %s, expires on %s",user->nick,parameters[0],
ServerInstance->TimeString(c_requires_crap).c_str());
}
}
else
{
user->WriteServ("NOTICE %s :*** E-Line for %s already exists",user->nick,parameters[0]);
}
}
else
{
if (ServerInstance->XLines->del_eline(parameters[0]))
{
FOREACH_MOD(I_OnDelELine,OnDelELine(user, parameters[0]));
ServerInstance->SNO->WriteToSnoMask('x',"%s Removed E-line on %s.",user->nick,parameters[0]);
}
else
{
user->WriteServ("NOTICE %s :*** E-Line %s not found in list, try /stats e.",user->nick,parameters[0]);
}
}
return CMD_SUCCESS;
}
\ No newline at end of file +/* +------------------------------------+ + * | 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 "users.h" +#include "modules.h" +#include "xline.h" +#include "commands/cmd_eline.h" + +extern "C" DllExport command_t* init_command(InspIRCd* Instance) +{ + return new cmd_eline(Instance); +} + +/** Handle /ELINE + */ +CmdResult cmd_eline::Handle (const char** parameters, int pcnt, userrec *user) +{ + if (pcnt >= 3) + { + IdentHostPair ih = ServerInstance->XLines->IdentSplit(parameters[0]); + if (ServerInstance->HostMatchesEveryone(ih.first+"@"+ih.second,user)) + return CMD_FAILURE; + + if (!strchr(parameters[0],'@')) + { + user->WriteServ("NOTICE %s :*** E-Line must contain a username, e.g. *@%s",user->nick,parameters[0]); + return CMD_FAILURE; + } + + long duration = ServerInstance->Duration(parameters[1]); + if (ServerInstance->XLines->add_eline(duration,user->nick,parameters[2],parameters[0])) + { + FOREACH_MOD(I_OnAddELine,OnAddELine(duration, user, parameters[2], parameters[0])); + + if (!duration) + { + ServerInstance->SNO->WriteToSnoMask('x',"%s added permanent E-line for %s.",user->nick,parameters[0]); + } + else + { + time_t c_requires_crap = duration + ServerInstance->Time(); + ServerInstance->SNO->WriteToSnoMask('x',"%s added timed E-line for %s, expires on %s",user->nick,parameters[0], + ServerInstance->TimeString(c_requires_crap).c_str()); + } + } + else + { + user->WriteServ("NOTICE %s :*** E-Line for %s already exists",user->nick,parameters[0]); + } + } + else + { + if (ServerInstance->XLines->del_eline(parameters[0])) + { + FOREACH_MOD(I_OnDelELine,OnDelELine(user, parameters[0])); + ServerInstance->SNO->WriteToSnoMask('x',"%s Removed E-line on %s.",user->nick,parameters[0]); + } + else + { + user->WriteServ("NOTICE %s :*** E-Line %s not found in list, try /stats e.",user->nick,parameters[0]); + } + } + + return CMD_SUCCESS; +} diff --git a/src/cmd_gline.cpp b/src/cmd_gline.cpp index c1632df82..f208d40bf 100644 --- a/src/cmd_gline.cpp +++ b/src/cmd_gline.cpp @@ -1 +1,89 @@ -/* +------------------------------------+
* | 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 "users.h"
#include "modules.h"
#include "xline.h"
#include "commands/cmd_gline.h"
extern "C" DllExport command_t* init_command(InspIRCd* Instance)
{
return new cmd_gline(Instance);
}
/** Handle /GLINE
*/
CmdResult cmd_gline::Handle (const char** parameters, int pcnt, userrec *user)
{
if (pcnt >= 3)
{
IdentHostPair ih = ServerInstance->XLines->IdentSplit(parameters[0]);
if (ServerInstance->HostMatchesEveryone(ih.first+"@"+ih.second,user))
return CMD_FAILURE;
if (!strchr(parameters[0],'@'))
{
user->WriteServ("NOTICE %s :*** G-Line must contain a username, e.g. *@%s",user->nick,parameters[0]);
return CMD_FAILURE;
}
else if (strchr(parameters[0],'!'))
{
user->WriteServ("NOTICE %s :*** G-Line cannot contain a nickname!",user->nick);
return CMD_FAILURE;
}
long duration = ServerInstance->Duration(parameters[1]);
if (ServerInstance->XLines->add_gline(duration,user->nick,parameters[2],parameters[0]))
{
int to_apply = APPLY_GLINES;
FOREACH_MOD(I_OnAddGLine,OnAddGLine(duration, user, parameters[2], parameters[0]));
if (!duration)
{
ServerInstance->SNO->WriteToSnoMask('x',"%s added permanent G-line for %s.",user->nick,parameters[0]);
to_apply |= APPLY_PERM_ONLY;
}
else
{
time_t c_requires_crap = duration + ServerInstance->Time();
ServerInstance->SNO->WriteToSnoMask('x',"%s added timed G-line for %s, expires on %s",user->nick,parameters[0],
ServerInstance->TimeString(c_requires_crap).c_str());
}
ServerInstance->XLines->apply_lines(to_apply);
}
else
{
user->WriteServ("NOTICE %s :*** G-Line for %s already exists",user->nick,parameters[0]);
}
}
else
{
if (ServerInstance->XLines->del_gline(parameters[0]))
{
FOREACH_MOD(I_OnDelGLine,OnDelGLine(user, parameters[0]));
ServerInstance->SNO->WriteToSnoMask('x',"%s Removed G-line on %s.",user->nick,parameters[0]);
}
else
{
user->WriteServ("NOTICE %s :*** G-line %s not found in list, try /stats g.",user->nick,parameters[0]);
}
}
return CMD_SUCCESS;
}
\ No newline at end of file +/* +------------------------------------+ + * | 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 "users.h" +#include "modules.h" +#include "xline.h" +#include "commands/cmd_gline.h" + +extern "C" DllExport command_t* init_command(InspIRCd* Instance) +{ + return new cmd_gline(Instance); +} + +/** Handle /GLINE + */ +CmdResult cmd_gline::Handle (const char** parameters, int pcnt, userrec *user) +{ + if (pcnt >= 3) + { + IdentHostPair ih = ServerInstance->XLines->IdentSplit(parameters[0]); + if (ServerInstance->HostMatchesEveryone(ih.first+"@"+ih.second,user)) + return CMD_FAILURE; + + if (!strchr(parameters[0],'@')) + { + user->WriteServ("NOTICE %s :*** G-Line must contain a username, e.g. *@%s",user->nick,parameters[0]); + return CMD_FAILURE; + } + else if (strchr(parameters[0],'!')) + { + user->WriteServ("NOTICE %s :*** G-Line cannot contain a nickname!",user->nick); + return CMD_FAILURE; + } + + long duration = ServerInstance->Duration(parameters[1]); + if (ServerInstance->XLines->add_gline(duration,user->nick,parameters[2],parameters[0])) + { + int to_apply = APPLY_GLINES; + + FOREACH_MOD(I_OnAddGLine,OnAddGLine(duration, user, parameters[2], parameters[0])); + + if (!duration) + { + ServerInstance->SNO->WriteToSnoMask('x',"%s added permanent G-line for %s.",user->nick,parameters[0]); + to_apply |= APPLY_PERM_ONLY; + } + else + { + time_t c_requires_crap = duration + ServerInstance->Time(); + ServerInstance->SNO->WriteToSnoMask('x',"%s added timed G-line for %s, expires on %s",user->nick,parameters[0], + ServerInstance->TimeString(c_requires_crap).c_str()); + } + + ServerInstance->XLines->apply_lines(to_apply); + } + else + { + user->WriteServ("NOTICE %s :*** G-Line for %s already exists",user->nick,parameters[0]); + } + + } + else + { + if (ServerInstance->XLines->del_gline(parameters[0])) + { + FOREACH_MOD(I_OnDelGLine,OnDelGLine(user, parameters[0])); + ServerInstance->SNO->WriteToSnoMask('x',"%s Removed G-line on %s.",user->nick,parameters[0]); + } + else + { + user->WriteServ("NOTICE %s :*** G-line %s not found in list, try /stats g.",user->nick,parameters[0]); + } + } + + return CMD_SUCCESS; +} + diff --git a/src/cmd_info.cpp b/src/cmd_info.cpp index de6c18d82..3564b0c6a 100644 --- a/src/cmd_info.cpp +++ b/src/cmd_info.cpp @@ -1 +1,75 @@ -/* +------------------------------------+
* | 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 "users.h"
#include "modules.h"
#include "commands/cmd_info.h"
extern "C" DllExport command_t* init_command(InspIRCd* Instance)
{
return new cmd_info(Instance);
}
/** Handle /INFO
*/
CmdResult cmd_info::Handle (const char** parameters, int pcnt, userrec *user)
{
user->WriteServ( "371 %s :. o O ( \2The Inspire Internet Relay Chat Server\2 ) O o .", user->nick);
user->WriteServ( "371 %s : ( \2Putting the ricer into ircer since 2007\2 )", user->nick);
user->WriteServ( "371 %s : ", user->nick);
user->WriteServ( "371 %s :\2Core Developers\2:", user->nick);
user->WriteServ( "371 %s : Craig Edwards (Brain)", user->nick);
user->WriteServ( "371 %s : Craig McLure", user->nick);
user->WriteServ( "371 %s : w00t", user->nick);
user->WriteServ( "371 %s : Om", user->nick);
user->WriteServ( "371 %s : Special", user->nick);
user->WriteServ( "371 %s : pippijn", user->nick);
user->WriteServ( "371 %s : peavey", user->nick);
user->WriteServ( "371 %s : Burlex", user->nick);
user->WriteServ( "371 %s : ", user->nick);
user->WriteServ( "371 %s :\2Contributors\2:", user->nick);
user->WriteServ( "371 %s : typobox43 Jazza", user->nick);
user->WriteServ( "371 %s : jamie LeaChim", user->nick);
user->WriteServ( "371 %s : satmd nenolod", user->nick);
user->WriteServ( "371 %s : HiroP BuildSmart", user->nick);
user->WriteServ( "371 %s : ", user->nick);
user->WriteServ( "371 %s :\2Quality Assurance\2:", user->nick);
user->WriteServ( "371 %s : Bricker owine", user->nick);
user->WriteServ( "371 %s : dmb Adremelech", user->nick);
user->WriteServ( "371 %s : ThePopeSVCD satmd", user->nick);
user->WriteServ( "371 %s :\2Testers\2:", user->nick);
user->WriteServ( "371 %s : CC Piggles", user->nick);
user->WriteServ( "371 %s : Foamy Hart", user->nick);
user->WriteServ( "371 %s : RageD [ed]", user->nick);
user->WriteServ( "371 %s : Azhrarn luigiman", user->nick);
user->WriteServ( "371 %s : Chu aquanight", user->nick);
user->WriteServ( "371 %s : xptek Grantlinks", user->nick);
user->WriteServ( "371 %s : Rob angelic", user->nick);
user->WriteServ( "371 %s : Jason ThaPrince", user->nick);
user->WriteServ( "371 %s : eggy skenmy", user->nick);
user->WriteServ( "371 %s : ", user->nick);
user->WriteServ( "371 %s :Contains portions of \2FireDNS\2 written by", user->nick);
user->WriteServ( "371 %s :Ian Gulliver, (c) 2002.", user->nick);
user->WriteServ( "371 %s : ", user->nick);
user->WriteServ( "371 %s :Thanks to \2irc-junkie\2 and \2SearchIRC\2", user->nick);
user->WriteServ( "371 %s :for the nice comments and the help", user->nick);
user->WriteServ( "371 %s :you gave us in attracting users to", user->nick);
user->WriteServ( "371 %s :this software.", user->nick);
user->WriteServ( "371 %s : ", user->nick);
user->WriteServ( "371 %s :Best experienced with: \2An IRC client\2.", user->nick);
FOREACH_MOD(I_OnInfo,OnInfo(user));
user->WriteServ( "374 %s :End of /INFO list", user->nick);
return CMD_SUCCESS;
}
\ No newline at end of file +/* +------------------------------------+ + * | 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 "users.h" +#include "modules.h" +#include "commands/cmd_info.h" + +extern "C" DllExport command_t* init_command(InspIRCd* Instance) +{ + return new cmd_info(Instance); +} + +/** Handle /INFO + */ +CmdResult cmd_info::Handle (const char** parameters, int pcnt, userrec *user) +{ + user->WriteServ( "371 %s :. o O ( \2The Inspire Internet Relay Chat Server\2 ) O o .", user->nick); + user->WriteServ( "371 %s : ( \2Putting the ricer into ircer since 2007\2 )", user->nick); + user->WriteServ( "371 %s : ", user->nick); + user->WriteServ( "371 %s :\2Core Developers\2:", user->nick); + user->WriteServ( "371 %s : Craig Edwards (Brain)", user->nick); + user->WriteServ( "371 %s : Craig McLure", user->nick); + user->WriteServ( "371 %s : w00t", user->nick); + user->WriteServ( "371 %s : Om", user->nick); + user->WriteServ( "371 %s : Special", user->nick); + user->WriteServ( "371 %s : pippijn", user->nick); + user->WriteServ( "371 %s : peavey", user->nick); + user->WriteServ( "371 %s : Burlex", user->nick); + user->WriteServ( "371 %s : ", user->nick); + user->WriteServ( "371 %s :\2Contributors\2:", user->nick); + user->WriteServ( "371 %s : typobox43 Jazza", user->nick); + user->WriteServ( "371 %s : jamie LeaChim", user->nick); + user->WriteServ( "371 %s : satmd nenolod", user->nick); + user->WriteServ( "371 %s : HiroP BuildSmart", user->nick); + user->WriteServ( "371 %s : ", user->nick); + user->WriteServ( "371 %s :\2Quality Assurance\2:", user->nick); + user->WriteServ( "371 %s : Bricker owine", user->nick); + user->WriteServ( "371 %s : dmb Adremelech", user->nick); + user->WriteServ( "371 %s : ThePopeSVCD satmd", user->nick); + user->WriteServ( "371 %s :\2Testers\2:", user->nick); + user->WriteServ( "371 %s : CC Piggles", user->nick); + user->WriteServ( "371 %s : Foamy Hart", user->nick); + user->WriteServ( "371 %s : RageD [ed]", user->nick); + user->WriteServ( "371 %s : Azhrarn luigiman", user->nick); + user->WriteServ( "371 %s : Chu aquanight", user->nick); + user->WriteServ( "371 %s : xptek Grantlinks", user->nick); + user->WriteServ( "371 %s : Rob angelic", user->nick); + user->WriteServ( "371 %s : Jason ThaPrince", user->nick); + user->WriteServ( "371 %s : eggy skenmy", user->nick); + user->WriteServ( "371 %s : ", user->nick); + user->WriteServ( "371 %s :Contains portions of \2FireDNS\2 written by", user->nick); + user->WriteServ( "371 %s :Ian Gulliver, (c) 2002.", user->nick); + user->WriteServ( "371 %s : ", user->nick); + user->WriteServ( "371 %s :Thanks to \2irc-junkie\2 and \2SearchIRC\2", user->nick); + user->WriteServ( "371 %s :for the nice comments and the help", user->nick); + user->WriteServ( "371 %s :you gave us in attracting users to", user->nick); + user->WriteServ( "371 %s :this software.", user->nick); + user->WriteServ( "371 %s : ", user->nick); + user->WriteServ( "371 %s :Best experienced with: \2An IRC client\2.", user->nick); + FOREACH_MOD(I_OnInfo,OnInfo(user)); + user->WriteServ( "374 %s :End of /INFO list", user->nick); + return CMD_SUCCESS; +} diff --git a/src/cmd_invite.cpp b/src/cmd_invite.cpp index 19330783e..0eb3a2354 100644 --- a/src/cmd_invite.cpp +++ b/src/cmd_invite.cpp @@ -1 +1,98 @@ -/* +------------------------------------+
* | 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 "users.h"
#include "modules.h"
#include "commands/cmd_invite.h"
extern "C" DllExport command_t* init_command(InspIRCd* Instance)
{
return new cmd_invite(Instance);
}
/** Handle /INVITE
*/
CmdResult cmd_invite::Handle (const char** parameters, int pcnt, userrec *user)
{
int MOD_RESULT = 0;
if (pcnt == 2)
{
userrec* u = ServerInstance->FindNick(parameters[0]);
chanrec* c = ServerInstance->FindChan(parameters[1]);
if ((!c) || (!u))
{
if (!c)
{
user->WriteServ("401 %s %s :No such nick/channel",user->nick, parameters[1]);
}
else
{
user->WriteServ("401 %s %s :No such nick/channel",user->nick, parameters[0]);
}
return CMD_FAILURE;
}
if ((c->modes[CM_INVITEONLY]) && (IS_LOCAL(user)))
{
if (c->GetStatus(user) < STATUS_HOP)
{
user->WriteServ("482 %s %s :You must be at least a half-operator to change modes on this channel",user->nick, c->name);
return CMD_FAILURE;
}
}
if (c->HasUser(u))
{
user->WriteServ("443 %s %s %s :is already on channel",user->nick,u->nick,c->name);
return CMD_FAILURE;
}
if ((IS_LOCAL(user)) && (!c->HasUser(user)))
{
user->WriteServ("442 %s %s :You're not on that channel!",user->nick, c->name);
return CMD_FAILURE;
}
FOREACH_RESULT(I_OnUserPreInvite,OnUserPreInvite(user,u,c));
if (MOD_RESULT == 1)
{
return CMD_FAILURE;
}
u->InviteTo(c->name);
u->WriteFrom(user,"INVITE %s :%s",u->nick,c->name);
user->WriteServ("341 %s %s %s",user->nick,u->nick,c->name);
if (ServerInstance->Config->AnnounceInvites)
c->WriteChannelWithServ(ServerInstance->Config->ServerName, "NOTICE %s :*** %s invited %s into the channel", c->name, user->nick, u->nick);
FOREACH_MOD(I_OnUserInvite,OnUserInvite(user,u,c));
}
else
{
// pinched from ircu - invite with not enough parameters shows channels
// youve been invited to but haven't joined yet.
InvitedList* il = user->GetInviteList();
for (InvitedList::iterator i = il->begin(); i != il->end(); i++)
{
user->WriteServ("346 %s :%s",user->nick,i->c_str());
}
user->WriteServ("347 %s :End of INVITE list",user->nick);
}
return CMD_SUCCESS;
}
\ No newline at end of file +/* +------------------------------------+ + * | 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 "users.h" +#include "modules.h" +#include "commands/cmd_invite.h" + +extern "C" DllExport command_t* init_command(InspIRCd* Instance) +{ + return new cmd_invite(Instance); +} + +/** Handle /INVITE + */ +CmdResult cmd_invite::Handle (const char** parameters, int pcnt, userrec *user) +{ + int MOD_RESULT = 0; + + if (pcnt == 2) + { + userrec* u = ServerInstance->FindNick(parameters[0]); + chanrec* c = ServerInstance->FindChan(parameters[1]); + + if ((!c) || (!u)) + { + if (!c) + { + user->WriteServ("401 %s %s :No such nick/channel",user->nick, parameters[1]); + } + else + { + user->WriteServ("401 %s %s :No such nick/channel",user->nick, parameters[0]); + } + + return CMD_FAILURE; + } + + if ((c->modes[CM_INVITEONLY]) && (IS_LOCAL(user))) + { + if (c->GetStatus(user) < STATUS_HOP) + { + user->WriteServ("482 %s %s :You must be at least a half-operator to change modes on this channel",user->nick, c->name); + return CMD_FAILURE; + } + } + + if (c->HasUser(u)) + { + user->WriteServ("443 %s %s %s :is already on channel",user->nick,u->nick,c->name); + return CMD_FAILURE; + } + + if ((IS_LOCAL(user)) && (!c->HasUser(user))) + { + user->WriteServ("442 %s %s :You're not on that channel!",user->nick, c->name); + return CMD_FAILURE; + } + + FOREACH_RESULT(I_OnUserPreInvite,OnUserPreInvite(user,u,c)); + + if (MOD_RESULT == 1) + { + return CMD_FAILURE; + } + + u->InviteTo(c->name); + u->WriteFrom(user,"INVITE %s :%s",u->nick,c->name); + user->WriteServ("341 %s %s %s",user->nick,u->nick,c->name); + if (ServerInstance->Config->AnnounceInvites) + c->WriteChannelWithServ(ServerInstance->Config->ServerName, "NOTICE %s :*** %s invited %s into the channel", c->name, user->nick, u->nick); + FOREACH_MOD(I_OnUserInvite,OnUserInvite(user,u,c)); + } + else + { + // pinched from ircu - invite with not enough parameters shows channels + // youve been invited to but haven't joined yet. + InvitedList* il = user->GetInviteList(); + for (InvitedList::iterator i = il->begin(); i != il->end(); i++) + { + user->WriteServ("346 %s :%s",user->nick,i->c_str()); + } + user->WriteServ("347 %s :End of INVITE list",user->nick); + } + return CMD_SUCCESS; +} + diff --git a/src/cmd_ison.cpp b/src/cmd_ison.cpp index f63019427..0cee160c7 100644 --- a/src/cmd_ison.cpp +++ b/src/cmd_ison.cpp @@ -1 +1,86 @@ -/* +------------------------------------+
* | 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 "users.h"
#include "commands/cmd_ison.h"
extern "C" DllExport command_t* init_command(InspIRCd* Instance)
{
return new cmd_ison(Instance);
}
/** Handle /ISON
*/
CmdResult cmd_ison::Handle (const char** parameters, int pcnt, userrec *user)
{
std::map<userrec*,userrec*> ison_already;
userrec *u;
std::string reply = std::string("303 ") + user->nick + " :";
for (int i = 0; i < pcnt; i++)
{
u = ServerInstance->FindNick(parameters[i]);
if (ison_already.find(u) != ison_already.end())
continue;
if (u)
{
reply.append(u->nick).append(" ");
if (reply.length() > 450)
{
user->WriteServ(reply);
reply = std::string("303 ") + user->nick + " :";
}
ison_already[u] = u;
}
else
{
if ((i == pcnt-1) && (strchr(parameters[i],' ')))
{
/* Its a space seperated list of nicks (RFC1459 says to support this)
*/
irc::spacesepstream list(parameters[i]);
std::string item("*");
while (((item = list.GetToken()) != ""))
{
u = ServerInstance->FindNick(item);
if (ison_already.find(u) != ison_already.end())
continue;
if (u)
{
if (u->Visibility && !u->Visibility->VisibleTo(user))
continue;
reply.append(u->nick).append(" ");
if (reply.length() > 450)
{
user->WriteServ(reply);
reply = std::string("303 ") + user->nick + " :";
}
ison_already[u] = u;
}
}
}
/* There will only be one of these, we can bail after. */
break;
}
}
if (!reply.empty())
user->WriteServ(reply);
return CMD_SUCCESS;
}
\ No newline at end of file +/* +------------------------------------+ + * | 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 "users.h" +#include "commands/cmd_ison.h" + +extern "C" DllExport command_t* init_command(InspIRCd* Instance) +{ + return new cmd_ison(Instance); +} + +/** Handle /ISON + */ +CmdResult cmd_ison::Handle (const char** parameters, int pcnt, userrec *user) +{ + std::map<userrec*,userrec*> ison_already; + userrec *u; + std::string reply = std::string("303 ") + user->nick + " :"; + + for (int i = 0; i < pcnt; i++) + { + u = ServerInstance->FindNick(parameters[i]); + if (ison_already.find(u) != ison_already.end()) + continue; + + if (u) + { + reply.append(u->nick).append(" "); + if (reply.length() > 450) + { + user->WriteServ(reply); + reply = std::string("303 ") + user->nick + " :"; + } + ison_already[u] = u; + } + else + { + if ((i == pcnt-1) && (strchr(parameters[i],' '))) + { + /* Its a space seperated list of nicks (RFC1459 says to support this) + */ + irc::spacesepstream list(parameters[i]); + std::string item("*"); + while (((item = list.GetToken()) != "")) + { + u = ServerInstance->FindNick(item); + if (ison_already.find(u) != ison_already.end()) + continue; + + if (u) + { + if (u->Visibility && !u->Visibility->VisibleTo(user)) + continue; + + reply.append(u->nick).append(" "); + if (reply.length() > 450) + { + user->WriteServ(reply); + reply = std::string("303 ") + user->nick + " :"; + } + ison_already[u] = u; + } + } + } + /* There will only be one of these, we can bail after. */ + break; + } + } + + if (!reply.empty()) + user->WriteServ(reply); + + return CMD_SUCCESS; +} + diff --git a/src/cmd_join.cpp b/src/cmd_join.cpp index 6780ac4be..218e0f7b8 100644 --- a/src/cmd_join.cpp +++ b/src/cmd_join.cpp @@ -1 +1,52 @@ -/* +------------------------------------+
* | 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 "users.h"
#include "commands/cmd_join.h"
extern "C" DllExport command_t* init_command(InspIRCd* Instance)
{
return new cmd_join(Instance);
}
/** Handle /JOIN
*/
CmdResult cmd_join::Handle (const char** parameters, int pcnt, userrec *user)
{
if (pcnt > 1)
{
if (ServerInstance->Parser->LoopCall(user, this, parameters, pcnt, 0, 1))
return CMD_SUCCESS;
if (ServerInstance->IsChannel(parameters[0]))
{
chanrec::JoinUser(ServerInstance, user, parameters[0], false, parameters[1]);
return CMD_SUCCESS;
}
}
else
{
if (ServerInstance->Parser->LoopCall(user, this, parameters, pcnt, 0))
return CMD_SUCCESS;
if (ServerInstance->IsChannel(parameters[0]))
{
chanrec::JoinUser(ServerInstance, user, parameters[0], false, "");
return CMD_SUCCESS;
}
}
user->WriteServ("403 %s %s :Invalid channel name",user->nick, parameters[0]);
return CMD_FAILURE;
}
\ No newline at end of file +/* +------------------------------------+ + * | 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 "users.h" +#include "commands/cmd_join.h" + +extern "C" DllExport command_t* init_command(InspIRCd* Instance) +{ + return new cmd_join(Instance); +} + +/** Handle /JOIN + */ +CmdResult cmd_join::Handle (const char** parameters, int pcnt, userrec *user) +{ + if (pcnt > 1) + { + if (ServerInstance->Parser->LoopCall(user, this, parameters, pcnt, 0, 1)) + return CMD_SUCCESS; + + if (ServerInstance->IsChannel(parameters[0])) + { + chanrec::JoinUser(ServerInstance, user, parameters[0], false, parameters[1]); + return CMD_SUCCESS; + } + } + else + { + if (ServerInstance->Parser->LoopCall(user, this, parameters, pcnt, 0)) + return CMD_SUCCESS; + + if (ServerInstance->IsChannel(parameters[0])) + { + chanrec::JoinUser(ServerInstance, user, parameters[0], false, ""); + return CMD_SUCCESS; + } + } + + user->WriteServ("403 %s %s :Invalid channel name",user->nick, parameters[0]); + return CMD_FAILURE; +} diff --git a/src/cmd_kick.cpp b/src/cmd_kick.cpp index 263cf35c5..17933a43f 100644 --- a/src/cmd_kick.cpp +++ b/src/cmd_kick.cpp @@ -1 +1,58 @@ -/* +------------------------------------+
* | 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 "users.h"
#include "inspircd.h"
#include "commands/cmd_kick.h"
extern "C" DllExport command_t* init_command(InspIRCd* Instance)
{
return new cmd_kick(Instance);
}
/** Handle /KICK
*/
CmdResult cmd_kick::Handle (const char** parameters, int pcnt, userrec *user)
{
char reason[MAXKICK];
chanrec* c = ServerInstance->FindChan(parameters[0]);
userrec* u = ServerInstance->FindNick(parameters[1]);
if (!u || !c)
{
user->WriteServ( "401 %s %s :No such nick/channel", user->nick, u ? parameters[0] : parameters[1]);
return CMD_FAILURE;
}
if ((IS_LOCAL(user)) && (!c->HasUser(user)) && (!ServerInstance->ULine(user->server)))
{
user->WriteServ( "442 %s %s :You're not on that channel!", user->nick, parameters[0]);
return CMD_FAILURE;
}
if (pcnt > 2)
{
strlcpy(reason, parameters[2], MAXKICK - 1);
}
else
{
strlcpy(reason, user->nick, MAXKICK - 1);
}
if (!c->KickUser(user, u, reason))
/* Nobody left here, delete the chanrec */
delete c;
return CMD_SUCCESS;
}
\ No newline at end of file +/* +------------------------------------+ + * | 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 "users.h" +#include "inspircd.h" +#include "commands/cmd_kick.h" + +extern "C" DllExport command_t* init_command(InspIRCd* Instance) +{ + return new cmd_kick(Instance); +} + +/** Handle /KICK + */ +CmdResult cmd_kick::Handle (const char** parameters, int pcnt, userrec *user) +{ + char reason[MAXKICK]; + chanrec* c = ServerInstance->FindChan(parameters[0]); + userrec* u = ServerInstance->FindNick(parameters[1]); + + if (!u || !c) + { + user->WriteServ( "401 %s %s :No such nick/channel", user->nick, u ? parameters[0] : parameters[1]); + return CMD_FAILURE; + } + + if ((IS_LOCAL(user)) && (!c->HasUser(user)) && (!ServerInstance->ULine(user->server))) + { + user->WriteServ( "442 %s %s :You're not on that channel!", user->nick, parameters[0]); + return CMD_FAILURE; + } + + if (pcnt > 2) + { + strlcpy(reason, parameters[2], MAXKICK - 1); + } + else + { + strlcpy(reason, user->nick, MAXKICK - 1); + } + + if (!c->KickUser(user, u, reason)) + /* Nobody left here, delete the chanrec */ + delete c; + + return CMD_SUCCESS; +} diff --git a/src/cmd_kill.cpp b/src/cmd_kill.cpp index b8dbfc345..26f354b8f 100644 --- a/src/cmd_kill.cpp +++ b/src/cmd_kill.cpp @@ -1 +1,117 @@ -/* +------------------------------------+
* | 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 "users.h"
#include "modules.h"
#include "wildcard.h"
#include "commands/cmd_kill.h"
extern "C" DllExport command_t* init_command(InspIRCd* Instance)
{
return new cmd_kill(Instance);
}
/** Handle /KILL
*/
CmdResult cmd_kill::Handle (const char** parameters, int pcnt, userrec *user)
{
/* Allow comma seperated lists of users for /KILL (thanks w00t) */
if (ServerInstance->Parser->LoopCall(user, this, parameters, pcnt, 0))
return CMD_SUCCESS;
userrec *u = ServerInstance->FindNick(parameters[0]);
char killreason[MAXBUF];
char killoperreason[MAXBUF];
int MOD_RESULT = 0;
if (u)
{
/*
* Here, we need to decide how to munge kill messages. Whether to hide killer, what to show opers, etc.
* We only do this when the command is being issued LOCALLY, for remote KILL, we just copy the message we got.
*
* This conditional is so that we only append the "Killed (" prefix ONCE. If killer is remote, then the kill
* just gets processed and passed on, otherwise, if they are local, it gets prefixed. Makes sense :-) -- w00t
*/
if (IS_LOCAL(user))
{
/*
* Moved this event inside the IS_LOCAL check also, we don't want half the network killing a user
* and the other half not. This would be a bad thing. ;p -- w00t
*/
FOREACH_RESULT(I_OnKill, OnKill(user, u, parameters[1]));
if (MOD_RESULT)
return CMD_FAILURE;
if (*ServerInstance->Config->HideKillsServer)
{
// hidekills is on, use it
snprintf(killreason, MAXQUIT, "Killed (%s (%s))", ServerInstance->Config->HideKillsServer, parameters[1]);
}
else
{
// hidekills is off, do nothing
snprintf(killreason, MAXQUIT, "Killed (%s (%s))", user->nick, parameters[1]);
}
// opers are lucky ducks, they always see the real reason
snprintf(killoperreason, MAXQUIT, "Killed (%s (%s))", user->nick, parameters[1]);
}
else
{
snprintf(killreason, MAXQUIT, "%s", parameters[1]);
/*
* XXX - yes, this means opers will probably see a censored kill remotely. this needs fixing.
* maybe a version of QuitUser that doesn't take nor propegate an oper reason? -- w00t
*/
snprintf(killoperreason, MAXQUIT, "%s", parameters[1]);
}
/*
* Now we need to decide whether or not to send a local or remote snotice. Currently this checking is a little flawed.
* No time to fix it right now, so left a note. -- w00t
*/
if (!IS_LOCAL(u))
{
// remote kill
ServerInstance->SNO->WriteToSnoMask('K', "Remote kill by %s: %s!%s@%s (%s)", user->nick, u->nick, u->ident, u->host, parameters[1]);
FOREACH_MOD(I_OnRemoteKill, OnRemoteKill(user, u, killreason, killoperreason));
}
else
{
// local kill
/*
* XXX - this isn't entirely correct, servers A - B - C, oper on A, client on C. Oper kills client, A and B will get remote kill
* snotices, C will get a local kill snotice. this isn't accurate, and needs fixing at some stage. -- w00t
*/
ServerInstance->SNO->WriteToSnoMask('k',"Local Kill by %s: %s!%s@%s (%s)", user->nick, u->nick, u->ident, u->host, parameters[1]);
ServerInstance->Log(DEFAULT,"LOCAL KILL: %s :%s!%s!%s (%s)", u->nick, ServerInstance->Config->ServerName, user->dhost, user->nick, parameters[1]);
user->WriteTo(u, "KILL %s :%s!%s!%s (%s)", u->nick, ServerInstance->Config->ServerName, user->dhost,
*ServerInstance->Config->HideKillsServer ? ServerInstance->Config->HideKillsServer : user->nick, parameters[1]);
}
// send the quit out
userrec::QuitUser(ServerInstance, u, killreason, killoperreason);
}
else
{
user->WriteServ( "401 %s %s :No such nick/channel", user->nick, parameters[0]);
return CMD_FAILURE;
}
return CMD_SUCCESS;
}
\ No newline at end of file +/* +------------------------------------+ + * | 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 "users.h" +#include "modules.h" +#include "wildcard.h" +#include "commands/cmd_kill.h" + +extern "C" DllExport command_t* init_command(InspIRCd* Instance) +{ + return new cmd_kill(Instance); +} + +/** Handle /KILL + */ +CmdResult cmd_kill::Handle (const char** parameters, int pcnt, userrec *user) +{ + /* Allow comma seperated lists of users for /KILL (thanks w00t) */ + if (ServerInstance->Parser->LoopCall(user, this, parameters, pcnt, 0)) + return CMD_SUCCESS; + + userrec *u = ServerInstance->FindNick(parameters[0]); + char killreason[MAXBUF]; + char killoperreason[MAXBUF]; + int MOD_RESULT = 0; + + if (u) + { + /* + * Here, we need to decide how to munge kill messages. Whether to hide killer, what to show opers, etc. + * We only do this when the command is being issued LOCALLY, for remote KILL, we just copy the message we got. + * + * This conditional is so that we only append the "Killed (" prefix ONCE. If killer is remote, then the kill + * just gets processed and passed on, otherwise, if they are local, it gets prefixed. Makes sense :-) -- w00t + */ + if (IS_LOCAL(user)) + { + /* + * Moved this event inside the IS_LOCAL check also, we don't want half the network killing a user + * and the other half not. This would be a bad thing. ;p -- w00t + */ + FOREACH_RESULT(I_OnKill, OnKill(user, u, parameters[1])); + + if (MOD_RESULT) + return CMD_FAILURE; + + if (*ServerInstance->Config->HideKillsServer) + { + // hidekills is on, use it + snprintf(killreason, MAXQUIT, "Killed (%s (%s))", ServerInstance->Config->HideKillsServer, parameters[1]); + } + else + { + // hidekills is off, do nothing + snprintf(killreason, MAXQUIT, "Killed (%s (%s))", user->nick, parameters[1]); + } + + // opers are lucky ducks, they always see the real reason + snprintf(killoperreason, MAXQUIT, "Killed (%s (%s))", user->nick, parameters[1]); + } + else + { + snprintf(killreason, MAXQUIT, "%s", parameters[1]); + /* + * XXX - yes, this means opers will probably see a censored kill remotely. this needs fixing. + * maybe a version of QuitUser that doesn't take nor propegate an oper reason? -- w00t + */ + snprintf(killoperreason, MAXQUIT, "%s", parameters[1]); + } + + /* + * Now we need to decide whether or not to send a local or remote snotice. Currently this checking is a little flawed. + * No time to fix it right now, so left a note. -- w00t + */ + if (!IS_LOCAL(u)) + { + // remote kill + ServerInstance->SNO->WriteToSnoMask('K', "Remote kill by %s: %s!%s@%s (%s)", user->nick, u->nick, u->ident, u->host, parameters[1]); + FOREACH_MOD(I_OnRemoteKill, OnRemoteKill(user, u, killreason, killoperreason)); + } + else + { + // local kill + /* + * XXX - this isn't entirely correct, servers A - B - C, oper on A, client on C. Oper kills client, A and B will get remote kill + * snotices, C will get a local kill snotice. this isn't accurate, and needs fixing at some stage. -- w00t + */ + ServerInstance->SNO->WriteToSnoMask('k',"Local Kill by %s: %s!%s@%s (%s)", user->nick, u->nick, u->ident, u->host, parameters[1]); + ServerInstance->Log(DEFAULT,"LOCAL KILL: %s :%s!%s!%s (%s)", u->nick, ServerInstance->Config->ServerName, user->dhost, user->nick, parameters[1]); + user->WriteTo(u, "KILL %s :%s!%s!%s (%s)", u->nick, ServerInstance->Config->ServerName, user->dhost, + *ServerInstance->Config->HideKillsServer ? ServerInstance->Config->HideKillsServer : user->nick, parameters[1]); + } + + // send the quit out + userrec::QuitUser(ServerInstance, u, killreason, killoperreason); + } + else + { + user->WriteServ( "401 %s %s :No such nick/channel", user->nick, parameters[0]); + return CMD_FAILURE; + } + + return CMD_SUCCESS; +} + diff --git a/src/cmd_kline.cpp b/src/cmd_kline.cpp index ac1aafe15..05856893a 100644 --- a/src/cmd_kline.cpp +++ b/src/cmd_kline.cpp @@ -1 +1,88 @@ -/* +------------------------------------+
* | 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 "users.h"
#include "modules.h"
#include "xline.h"
#include "commands/cmd_kline.h"
extern "C" DllExport command_t* init_command(InspIRCd* Instance)
{
return new cmd_kline(Instance);
}
/** Handle /KLINE
*/
CmdResult cmd_kline::Handle (const char** parameters, int pcnt, userrec *user)
{
if (pcnt >= 3)
{
IdentHostPair ih = ServerInstance->XLines->IdentSplit(parameters[0]);
if (ServerInstance->HostMatchesEveryone(ih.first+"@"+ih.second,user))
return CMD_FAILURE;
if (!strchr(parameters[0],'@'))
{
user->WriteServ("NOTICE %s :*** K-Line must contain a username, e.g. *@%s",user->nick,parameters[0]);
return CMD_FAILURE;
}
else if (strchr(parameters[0],'!'))
{
user->WriteServ("NOTICE %s :*** K-Line cannot contain a nickname!",user->nick);
return CMD_FAILURE;
}
long duration = ServerInstance->Duration(parameters[1]);
if (ServerInstance->XLines->add_kline(duration,user->nick,parameters[2],parameters[0]))
{
int to_apply = APPLY_KLINES;
FOREACH_MOD(I_OnAddKLine,OnAddKLine(duration, user, parameters[2], parameters[0]));
if (!duration)
{
ServerInstance->SNO->WriteToSnoMask('x',"%s added permanent K-line for %s.",user->nick,parameters[0]);
to_apply |= APPLY_PERM_ONLY;
}
else
{
time_t c_requires_crap = duration + ServerInstance->Time();
ServerInstance->SNO->WriteToSnoMask('x',"%s added timed K-line for %s, expires on %s",user->nick,parameters[0],
ServerInstance->TimeString(c_requires_crap).c_str());
}
ServerInstance->XLines->apply_lines(to_apply);
}
else
{
user->WriteServ("NOTICE %s :*** K-Line for %s already exists",user->nick,parameters[0]);
}
}
else
{
if (ServerInstance->XLines->del_kline(parameters[0]))
{
FOREACH_MOD(I_OnDelKLine,OnDelKLine(user, parameters[0]));
ServerInstance->SNO->WriteToSnoMask('x',"%s Removed K-line on %s.",user->nick,parameters[0]);
}
else
{
user->WriteServ("NOTICE %s :*** K-Line %s not found in list, try /stats k.",user->nick,parameters[0]);
}
}
return CMD_SUCCESS;
}
\ No newline at end of file +/* +------------------------------------+ + * | 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 "users.h" +#include "modules.h" +#include "xline.h" +#include "commands/cmd_kline.h" + +extern "C" DllExport command_t* init_command(InspIRCd* Instance) +{ + return new cmd_kline(Instance); +} + +/** Handle /KLINE + */ +CmdResult cmd_kline::Handle (const char** parameters, int pcnt, userrec *user) +{ + if (pcnt >= 3) + { + IdentHostPair ih = ServerInstance->XLines->IdentSplit(parameters[0]); + if (ServerInstance->HostMatchesEveryone(ih.first+"@"+ih.second,user)) + return CMD_FAILURE; + + if (!strchr(parameters[0],'@')) + { + user->WriteServ("NOTICE %s :*** K-Line must contain a username, e.g. *@%s",user->nick,parameters[0]); + return CMD_FAILURE; + } + else if (strchr(parameters[0],'!')) + { + user->WriteServ("NOTICE %s :*** K-Line cannot contain a nickname!",user->nick); + return CMD_FAILURE; + } + + long duration = ServerInstance->Duration(parameters[1]); + if (ServerInstance->XLines->add_kline(duration,user->nick,parameters[2],parameters[0])) + { + int to_apply = APPLY_KLINES; + + FOREACH_MOD(I_OnAddKLine,OnAddKLine(duration, user, parameters[2], parameters[0])); + + if (!duration) + { + ServerInstance->SNO->WriteToSnoMask('x',"%s added permanent K-line for %s.",user->nick,parameters[0]); + to_apply |= APPLY_PERM_ONLY; + } + else + { + time_t c_requires_crap = duration + ServerInstance->Time(); + ServerInstance->SNO->WriteToSnoMask('x',"%s added timed K-line for %s, expires on %s",user->nick,parameters[0], + ServerInstance->TimeString(c_requires_crap).c_str()); + } + + ServerInstance->XLines->apply_lines(to_apply); + } + else + { + user->WriteServ("NOTICE %s :*** K-Line for %s already exists",user->nick,parameters[0]); + } + } + else + { + if (ServerInstance->XLines->del_kline(parameters[0])) + { + FOREACH_MOD(I_OnDelKLine,OnDelKLine(user, parameters[0])); + ServerInstance->SNO->WriteToSnoMask('x',"%s Removed K-line on %s.",user->nick,parameters[0]); + } + else + { + user->WriteServ("NOTICE %s :*** K-Line %s not found in list, try /stats k.",user->nick,parameters[0]); + } + } + + return CMD_SUCCESS; +} + diff --git a/src/cmd_links.cpp b/src/cmd_links.cpp index 39f01383e..83e558d5d 100644 --- a/src/cmd_links.cpp +++ b/src/cmd_links.cpp @@ -1 +1,32 @@ -/* +------------------------------------+
* | 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 "users.h"
#include "modules.h"
#include "commands/cmd_links.h"
extern "C" DllExport command_t* init_command(InspIRCd* Instance)
{
return new cmd_links(Instance);
}
/** Handle /LINKS
*/
CmdResult cmd_links::Handle (const char** parameters, int pcnt, userrec *user)
{
user->WriteServ("364 %s %s %s :0 %s",user->nick,ServerInstance->Config->ServerName,ServerInstance->Config->ServerName,ServerInstance->Config->ServerDesc);
user->WriteServ("365 %s * :End of /LINKS list.",user->nick);
return CMD_SUCCESS;
}
\ No newline at end of file +/* +------------------------------------+ + * | 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 "users.h" +#include "modules.h" +#include "commands/cmd_links.h" + +extern "C" DllExport command_t* init_command(InspIRCd* Instance) +{ + return new cmd_links(Instance); +} + +/** Handle /LINKS + */ +CmdResult cmd_links::Handle (const char** parameters, int pcnt, userrec *user) +{ + user->WriteServ("364 %s %s %s :0 %s",user->nick,ServerInstance->Config->ServerName,ServerInstance->Config->ServerName,ServerInstance->Config->ServerDesc); + user->WriteServ("365 %s * :End of /LINKS list.",user->nick); + return CMD_SUCCESS; +} diff --git a/src/cmd_list.cpp b/src/cmd_list.cpp index 14cf45272..ed956e7f1 100644 --- a/src/cmd_list.cpp +++ b/src/cmd_list.cpp @@ -1 +1,85 @@ -/* +------------------------------------+
* | 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 "users.h"
#include "inspircd.h"
#include "commands/cmd_list.h"
#include "wildcard.h"
/** Handle /LIST
*/
extern "C" DllExport command_t* init_command(InspIRCd* Instance)
{
return new cmd_list(Instance);
}
CmdResult cmd_list::Handle (const char** parameters, int pcnt, userrec *user)
{
int minusers = 0, maxusers = 0;
user->WriteServ("321 %s Channel :Users Name",user->nick);
/* Work around mIRC suckyness. YOU SUCK, KHALED! */
if (pcnt == 1)
{
if (*parameters[0] == '<')
{
maxusers = atoi(parameters[0]+1);
pcnt = 0;
}
else if (*parameters[0] == '>')
{
minusers = atoi(parameters[0]+1);
pcnt = 0;
}
}
for (chan_hash::const_iterator i = ServerInstance->chanlist->begin(); i != ServerInstance->chanlist->end(); i++)
{
// attempt to match a glob pattern
long users = i->second->GetUserCounter();
bool too_few = (minusers && (users <= minusers));
bool too_many = (maxusers && (users >= maxusers));
if (too_many || too_few)
continue;
if (pcnt)
{
if (!match(i->second->name, parameters[0]) && !match(i->second->topic, parameters[0]))
continue;
}
// if the channel is not private/secret, OR the user is on the channel anyway
bool n = i->second->HasUser(user);
if ((i->second->modes[CM_PRIVATE]) && (!n))
{
if (users)
user->WriteServ("322 %s *",user->nick,i->second->name);
}
else
{
if (((!(i->second->modes[CM_PRIVATE])) && (!(i->second->modes[CM_SECRET]))) || (n))
{
long users = i->second->GetUserCounter();
if (users)
user->WriteServ("322 %s %s %d :[+%s] %s",user->nick,i->second->name,users,i->second->ChanModes(n),i->second->topic);
}
}
}
user->WriteServ("323 %s :End of channel list.",user->nick);
return CMD_SUCCESS;
}
\ No newline at end of file +/* +------------------------------------+ + * | 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 "users.h" +#include "inspircd.h" +#include "commands/cmd_list.h" +#include "wildcard.h" + +/** Handle /LIST + */ +extern "C" DllExport command_t* init_command(InspIRCd* Instance) +{ + return new cmd_list(Instance); +} + +CmdResult cmd_list::Handle (const char** parameters, int pcnt, userrec *user) +{ + int minusers = 0, maxusers = 0; + + user->WriteServ("321 %s Channel :Users Name",user->nick); + + /* Work around mIRC suckyness. YOU SUCK, KHALED! */ + if (pcnt == 1) + { + if (*parameters[0] == '<') + { + maxusers = atoi(parameters[0]+1); + pcnt = 0; + } + else if (*parameters[0] == '>') + { + minusers = atoi(parameters[0]+1); + pcnt = 0; + } + } + + for (chan_hash::const_iterator i = ServerInstance->chanlist->begin(); i != ServerInstance->chanlist->end(); i++) + { + // attempt to match a glob pattern + long users = i->second->GetUserCounter(); + + bool too_few = (minusers && (users <= minusers)); + bool too_many = (maxusers && (users >= maxusers)); + + if (too_many || too_few) + continue; + + if (pcnt) + { + if (!match(i->second->name, parameters[0]) && !match(i->second->topic, parameters[0])) + continue; + } + + // if the channel is not private/secret, OR the user is on the channel anyway + bool n = i->second->HasUser(user); + if ((i->second->modes[CM_PRIVATE]) && (!n)) + { + if (users) + user->WriteServ("322 %s *",user->nick,i->second->name); + } + else + { + if (((!(i->second->modes[CM_PRIVATE])) && (!(i->second->modes[CM_SECRET]))) || (n)) + { + long users = i->second->GetUserCounter(); + if (users) + user->WriteServ("322 %s %s %d :[+%s] %s",user->nick,i->second->name,users,i->second->ChanModes(n),i->second->topic); + } + } + } + user->WriteServ("323 %s :End of channel list.",user->nick); + + return CMD_SUCCESS; +} diff --git a/src/cmd_loadmodule.cpp b/src/cmd_loadmodule.cpp index 07776c1a8..08179d120 100644 --- a/src/cmd_loadmodule.cpp +++ b/src/cmd_loadmodule.cpp @@ -1 +1,39 @@ -/* +------------------------------------+
* | 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 "users.h"
#include "commands/cmd_loadmodule.h"
extern "C" DllExport command_t* init_command(InspIRCd* Instance)
{
return new cmd_loadmodule(Instance);
}
/** Handle /LOADMODULE
*/
CmdResult cmd_loadmodule::Handle (const char** parameters, int pcnt, userrec *user)
{
if (ServerInstance->LoadModule(parameters[0]))
{
ServerInstance->WriteOpers("*** NEW MODULE: %s loaded %s",user->nick, parameters[0]);
user->WriteServ("975 %s %s :Module successfully loaded.",user->nick, parameters[0]);
return CMD_SUCCESS;
}
else
{
user->WriteServ("974 %s %s :Failed to load module: %s",user->nick, parameters[0],ServerInstance->ModuleError());
return CMD_FAILURE;
}
}
\ No newline at end of file +/* +------------------------------------+ + * | 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 "users.h" +#include "commands/cmd_loadmodule.h" + +extern "C" DllExport command_t* init_command(InspIRCd* Instance) +{ + return new cmd_loadmodule(Instance); +} + +/** Handle /LOADMODULE + */ +CmdResult cmd_loadmodule::Handle (const char** parameters, int pcnt, userrec *user) +{ + if (ServerInstance->LoadModule(parameters[0])) + { + ServerInstance->WriteOpers("*** NEW MODULE: %s loaded %s",user->nick, parameters[0]); + user->WriteServ("975 %s %s :Module successfully loaded.",user->nick, parameters[0]); + return CMD_SUCCESS; + } + else + { + user->WriteServ("974 %s %s :Failed to load module: %s",user->nick, parameters[0],ServerInstance->ModuleError()); + return CMD_FAILURE; + } +} + diff --git a/src/cmd_lusers.cpp b/src/cmd_lusers.cpp index 87194d9e2..fa58ca076 100644 --- a/src/cmd_lusers.cpp +++ b/src/cmd_lusers.cpp @@ -1 +1,42 @@ -/* +------------------------------------+
* | 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 "users.h"
#include "inspircd.h"
#include "commands/cmd_lusers.h"
extern "C" DllExport command_t* init_command(InspIRCd* Instance)
{
return new cmd_lusers(Instance);
}
/** Handle /LUSERS
*/
CmdResult cmd_lusers::Handle (const char** parameters, int pcnt, userrec *user)
{
// this lusers command shows one server at all times because
// a protocol module must override it to show those stats.
user->WriteServ("251 %s :There are %d users and %d invisible on 1 server",user->nick,ServerInstance->UserCount()-ServerInstance->InvisibleUserCount(),ServerInstance->InvisibleUserCount());
if (ServerInstance->OperCount())
user->WriteServ("252 %s %d :operator(s) online",user->nick,ServerInstance->OperCount());
if (ServerInstance->UnregisteredUserCount())
user->WriteServ("253 %s %d :unknown connections",user->nick,ServerInstance->UnregisteredUserCount());
if (ServerInstance->ChannelCount())
user->WriteServ("254 %s %d :channels formed",user->nick,ServerInstance->ChannelCount());
if (ServerInstance->LocalUserCount())
user->WriteServ("255 %s :I have %d clients and 0 servers",user->nick,ServerInstance->LocalUserCount());
return CMD_SUCCESS;
}
\ No newline at end of file +/* +------------------------------------+ + * | 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 "users.h" +#include "inspircd.h" +#include "commands/cmd_lusers.h" + +extern "C" DllExport command_t* init_command(InspIRCd* Instance) +{ + return new cmd_lusers(Instance); +} + +/** Handle /LUSERS + */ +CmdResult cmd_lusers::Handle (const char** parameters, int pcnt, userrec *user) +{ + // this lusers command shows one server at all times because + // a protocol module must override it to show those stats. + user->WriteServ("251 %s :There are %d users and %d invisible on 1 server",user->nick,ServerInstance->UserCount()-ServerInstance->InvisibleUserCount(),ServerInstance->InvisibleUserCount()); + if (ServerInstance->OperCount()) + user->WriteServ("252 %s %d :operator(s) online",user->nick,ServerInstance->OperCount()); + if (ServerInstance->UnregisteredUserCount()) + user->WriteServ("253 %s %d :unknown connections",user->nick,ServerInstance->UnregisteredUserCount()); + if (ServerInstance->ChannelCount()) + user->WriteServ("254 %s %d :channels formed",user->nick,ServerInstance->ChannelCount()); + if (ServerInstance->LocalUserCount()) + user->WriteServ("255 %s :I have %d clients and 0 servers",user->nick,ServerInstance->LocalUserCount()); + + return CMD_SUCCESS; +} + diff --git a/src/cmd_map.cpp b/src/cmd_map.cpp index 270374cbf..c75a18b3f 100644 --- a/src/cmd_map.cpp +++ b/src/cmd_map.cpp @@ -1 +1,35 @@ -/* +------------------------------------+
* | 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 "users.h"
#include "commands/cmd_map.h"
extern "C" DllExport command_t* init_command(InspIRCd* Instance)
{
return new cmd_map(Instance);
}
/** Handle /MAP
*/
CmdResult cmd_map::Handle (const char** parameters, int pcnt, userrec *user)
{
// as with /LUSERS this does nothing without a linking
// module to override its behaviour and display something
// better.
user->WriteServ("006 %s :%s",user->nick,ServerInstance->Config->ServerName);
user->WriteServ("007 %s :End of /MAP",user->nick);
return CMD_SUCCESS;
}
\ No newline at end of file +/* +------------------------------------+ + * | 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 "users.h" +#include "commands/cmd_map.h" + +extern "C" DllExport command_t* init_command(InspIRCd* Instance) +{ + return new cmd_map(Instance); +} + +/** Handle /MAP + */ +CmdResult cmd_map::Handle (const char** parameters, int pcnt, userrec *user) +{ + // as with /LUSERS this does nothing without a linking + // module to override its behaviour and display something + // better. + user->WriteServ("006 %s :%s",user->nick,ServerInstance->Config->ServerName); + user->WriteServ("007 %s :End of /MAP",user->nick); + + return CMD_SUCCESS; +} diff --git a/src/cmd_mode.cpp b/src/cmd_mode.cpp index 563fa75db..18c1e69ad 100644 --- a/src/cmd_mode.cpp +++ b/src/cmd_mode.cpp @@ -1 +1,31 @@ -/* +------------------------------------+
* | 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 "users.h"
#include "commands/cmd_mode.h"
extern "C" DllExport command_t* init_command(InspIRCd* Instance)
{
return new cmd_mode(Instance);
}
/** Handle /MODE
*/
CmdResult cmd_mode::Handle (const char** parameters, int pcnt, userrec *user)
{
ServerInstance->Modes->Process(parameters, pcnt, user, false);
return CMD_SUCCESS;
}
\ No newline at end of file +/* +------------------------------------+ + * | 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 "users.h" +#include "commands/cmd_mode.h" + +extern "C" DllExport command_t* init_command(InspIRCd* Instance) +{ + return new cmd_mode(Instance); +} + +/** Handle /MODE + */ +CmdResult cmd_mode::Handle (const char** parameters, int pcnt, userrec *user) +{ + ServerInstance->Modes->Process(parameters, pcnt, user, false); + return CMD_SUCCESS; +} + diff --git a/src/cmd_modules.cpp b/src/cmd_modules.cpp index b8812c22d..be236fcd9 100644 --- a/src/cmd_modules.cpp +++ b/src/cmd_modules.cpp @@ -1 +1,75 @@ -/* +------------------------------------+
* | 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 "users.h"
#include "modules.h"
#include "wildcard.h"
#include "commands/cmd_modules.h"
char* itab[] = {
"OnUserConnect", "OnUserQuit", "OnUserDisconnect", "OnUserJoin", "OnUserPart", "OnRehash", "OnServerRaw",
"OnUserPreJoin", "OnUserPreKick", "OnUserKick", "OnOper", "OnInfo", "OnWhois", "OnUserPreInvite",
"OnUserInvite", "OnUserPreMessage", "OnUserPreNotice", "OnUserPreNick", "OnUserMessage", "OnUserNotice", "OnMode",
"OnGetServerDescription", "OnSyncUser", "OnSyncChannel", "OnSyncChannelMetaData", "OnSyncUserMetaData",
"OnDecodeMetaData", "ProtoSendMode", "ProtoSendMetaData", "OnWallops", "OnChangeHost", "OnChangeName", "OnAddGLine",
"OnAddZLine", "OnAddQLine", "OnAddKLine", "OnAddELine", "OnDelGLine", "OnDelZLine", "OnDelKLine", "OnDelELine", "OnDelQLine",
"OnCleanup", "OnUserPostNick", "OnAccessCheck", "On005Numeric", "OnKill", "OnRemoteKill", "OnLoadModule", "OnUnloadModule",
"OnBackgroundTimer", "OnSendList", "OnPreCommand", "OnCheckReady", "OnUserRegister", "OnCheckInvite",
"OnCheckKey", "OnCheckLimit", "OnCheckBan", "OnStats", "OnChangeLocalUserHost", "OnChangeLocalUserGecos", "OnLocalTopicChange",
"OnPostLocalTopicChange", "OnEvent", "OnRequest", "OnOperCompre", "OnGlobalOper", "OnPostConnect", "OnAddBan", "OnDelBan",
"OnRawSocketAccept", "OnRawSocketClose", "OnRawSocketWrite", "OnRawSocketRead", "OnChangeLocalUserGECOS", "OnUserRegister",
"OnOperCompare", "OnChannelDelete", "OnPostOper", "OnSyncOtherMetaData", "OnSetAway", "OnCancelAway", "OnNamesList",
"OnPostCommand", "OnPostJoin", "OnWhoisLine", "OnBuildExemptList", "OnRawSocketConnect", "OnGarbageCollect", NULL
};
extern "C" DllExport command_t* init_command(InspIRCd* Instance)
{
return new cmd_modules(Instance);
}
/** Handle /MODULES
*/
CmdResult cmd_modules::Handle (const char** parameters, int pcnt, userrec *user)
{
for (unsigned int i = 0; i < ServerInstance->Config->module_names.size(); i++)
{
Version V = ServerInstance->modules[i]->GetVersion();
char modulename[MAXBUF];
char flagstate[MAXBUF];
*flagstate = 0;
if (V.Flags & VF_STATIC)
strlcat(flagstate,", static",MAXBUF);
if (V.Flags & VF_VENDOR)
strlcat(flagstate,", vendor",MAXBUF);
if (V.Flags & VF_COMMON)
strlcat(flagstate,", common",MAXBUF);
if (V.Flags & VF_SERVICEPROVIDER)
strlcat(flagstate,", service provider",MAXBUF);
if (!flagstate[0])
strcpy(flagstate," <no flags>");
strlcpy(modulename,ServerInstance->Config->module_names[i].c_str(),256);
if (IS_OPER(user))
{
user->WriteServ("900 %s :0x%08lx %d.%d.%d.%d %s (%s)",user->nick,ServerInstance->modules[i],V.Major,V.Minor,V.Revision,V.Build,ServerConfig::CleanFilename(modulename),flagstate+2);
}
else
{
user->WriteServ("900 %s :%s",user->nick,ServerConfig::CleanFilename(modulename));
}
}
user->WriteServ("901 %s :End of MODULES list",user->nick);
return CMD_SUCCESS;
}
\ No newline at end of file +/* +------------------------------------+ + * | 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 "users.h" +#include "modules.h" +#include "wildcard.h" +#include "commands/cmd_modules.h" + +char* itab[] = { + "OnUserConnect", "OnUserQuit", "OnUserDisconnect", "OnUserJoin", "OnUserPart", "OnRehash", "OnServerRaw", + "OnUserPreJoin", "OnUserPreKick", "OnUserKick", "OnOper", "OnInfo", "OnWhois", "OnUserPreInvite", + "OnUserInvite", "OnUserPreMessage", "OnUserPreNotice", "OnUserPreNick", "OnUserMessage", "OnUserNotice", "OnMode", + "OnGetServerDescription", "OnSyncUser", "OnSyncChannel", "OnSyncChannelMetaData", "OnSyncUserMetaData", + "OnDecodeMetaData", "ProtoSendMode", "ProtoSendMetaData", "OnWallops", "OnChangeHost", "OnChangeName", "OnAddGLine", + "OnAddZLine", "OnAddQLine", "OnAddKLine", "OnAddELine", "OnDelGLine", "OnDelZLine", "OnDelKLine", "OnDelELine", "OnDelQLine", + "OnCleanup", "OnUserPostNick", "OnAccessCheck", "On005Numeric", "OnKill", "OnRemoteKill", "OnLoadModule", "OnUnloadModule", + "OnBackgroundTimer", "OnSendList", "OnPreCommand", "OnCheckReady", "OnUserRegister", "OnCheckInvite", + "OnCheckKey", "OnCheckLimit", "OnCheckBan", "OnStats", "OnChangeLocalUserHost", "OnChangeLocalUserGecos", "OnLocalTopicChange", + "OnPostLocalTopicChange", "OnEvent", "OnRequest", "OnOperCompre", "OnGlobalOper", "OnPostConnect", "OnAddBan", "OnDelBan", + "OnRawSocketAccept", "OnRawSocketClose", "OnRawSocketWrite", "OnRawSocketRead", "OnChangeLocalUserGECOS", "OnUserRegister", + "OnOperCompare", "OnChannelDelete", "OnPostOper", "OnSyncOtherMetaData", "OnSetAway", "OnCancelAway", "OnNamesList", + "OnPostCommand", "OnPostJoin", "OnWhoisLine", "OnBuildExemptList", "OnRawSocketConnect", "OnGarbageCollect", NULL +}; + +extern "C" DllExport command_t* init_command(InspIRCd* Instance) +{ + return new cmd_modules(Instance); +} + +/** Handle /MODULES + */ +CmdResult cmd_modules::Handle (const char** parameters, int pcnt, userrec *user) +{ + for (unsigned int i = 0; i < ServerInstance->Config->module_names.size(); i++) + { + Version V = ServerInstance->modules[i]->GetVersion(); + char modulename[MAXBUF]; + char flagstate[MAXBUF]; + *flagstate = 0; + if (V.Flags & VF_STATIC) + strlcat(flagstate,", static",MAXBUF); + if (V.Flags & VF_VENDOR) + strlcat(flagstate,", vendor",MAXBUF); + if (V.Flags & VF_COMMON) + strlcat(flagstate,", common",MAXBUF); + if (V.Flags & VF_SERVICEPROVIDER) + strlcat(flagstate,", service provider",MAXBUF); + if (!flagstate[0]) + strcpy(flagstate," <no flags>"); + strlcpy(modulename,ServerInstance->Config->module_names[i].c_str(),256); + if (IS_OPER(user)) + { + user->WriteServ("900 %s :0x%08lx %d.%d.%d.%d %s (%s)",user->nick,ServerInstance->modules[i],V.Major,V.Minor,V.Revision,V.Build,ServerConfig::CleanFilename(modulename),flagstate+2); + } + else + { + user->WriteServ("900 %s :%s",user->nick,ServerConfig::CleanFilename(modulename)); + } + } + user->WriteServ("901 %s :End of MODULES list",user->nick); + + return CMD_SUCCESS; +} diff --git a/src/cmd_motd.cpp b/src/cmd_motd.cpp index f14af0bd1..6aaf1a9af 100644 --- a/src/cmd_motd.cpp +++ b/src/cmd_motd.cpp @@ -1 +1,29 @@ -/* +------------------------------------+
* | 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 "users.h"
#include "commands/cmd_motd.h"
extern "C" DllExport command_t* init_command(InspIRCd* Instance)
{
return new cmd_motd(Instance);
}
/** Handle /MOTD
*/
CmdResult cmd_motd::Handle (const char** parameters, int pcnt, userrec *user)
{
user->ShowMOTD();
return CMD_SUCCESS;
}
\ No newline at end of file +/* +------------------------------------+ + * | 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 "users.h" +#include "commands/cmd_motd.h" + +extern "C" DllExport command_t* init_command(InspIRCd* Instance) +{ + return new cmd_motd(Instance); +} + +/** Handle /MOTD + */ +CmdResult cmd_motd::Handle (const char** parameters, int pcnt, userrec *user) +{ + user->ShowMOTD(); + return CMD_SUCCESS; +} diff --git a/src/cmd_names.cpp b/src/cmd_names.cpp index c157c3c04..fe61c4dea 100644 --- a/src/cmd_names.cpp +++ b/src/cmd_names.cpp @@ -1 +1,54 @@ -/* +------------------------------------+
* | 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 "users.h"
#include "commands/cmd_names.h"
extern "C" DllExport command_t* init_command(InspIRCd* Instance)
{
return new cmd_names(Instance);
}
/** Handle /NAMES
*/
CmdResult cmd_names::Handle (const char** parameters, int pcnt, userrec *user)
{
chanrec* c;
if (!pcnt)
{
user->WriteServ("366 %s * :End of /NAMES list.",user->nick);
return CMD_SUCCESS;
}
if (ServerInstance->Parser->LoopCall(user, this, parameters, pcnt, 0))
return CMD_SUCCESS;
c = ServerInstance->FindChan(parameters[0]);
if (c)
{
if ((c->modes[CM_SECRET]) && (!c->HasUser(user)))
{
user->WriteServ("401 %s %s :No such nick/channel",user->nick, c->name);
return CMD_FAILURE;
}
c->UserList(user);
}
else
{
user->WriteServ("401 %s %s :No such nick/channel",user->nick, parameters[0]);
}
return CMD_SUCCESS;
}
\ No newline at end of file +/* +------------------------------------+ + * | 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 "users.h" +#include "commands/cmd_names.h" + +extern "C" DllExport command_t* init_command(InspIRCd* Instance) +{ + return new cmd_names(Instance); +} + +/** Handle /NAMES + */ +CmdResult cmd_names::Handle (const char** parameters, int pcnt, userrec *user) +{ + chanrec* c; + + if (!pcnt) + { + user->WriteServ("366 %s * :End of /NAMES list.",user->nick); + return CMD_SUCCESS; + } + + if (ServerInstance->Parser->LoopCall(user, this, parameters, pcnt, 0)) + return CMD_SUCCESS; + + c = ServerInstance->FindChan(parameters[0]); + if (c) + { + if ((c->modes[CM_SECRET]) && (!c->HasUser(user))) + { + user->WriteServ("401 %s %s :No such nick/channel",user->nick, c->name); + return CMD_FAILURE; + } + c->UserList(user); + } + else + { + user->WriteServ("401 %s %s :No such nick/channel",user->nick, parameters[0]); + } + + return CMD_SUCCESS; +} diff --git a/src/cmd_nick.cpp b/src/cmd_nick.cpp index 5bc9ce6d1..4e2ebcf6e 100644 --- a/src/cmd_nick.cpp +++ b/src/cmd_nick.cpp @@ -1 +1,189 @@ -/* +------------------------------------+
* | 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 "users.h"
#include "modules.h"
#include "xline.h"
#include "commands/cmd_nick.h"
extern "C" DllExport command_t* init_command(InspIRCd* Instance)
{
return new cmd_nick(Instance);
}
/** Handle nick changes from users.
* NOTE: If you are used to ircds based on ircd2.8, and are looking
* for the client introduction code in here, youre in the wrong place.
* You need to look in the spanningtree module for this!
*/
CmdResult cmd_nick::Handle (const char** parameters, int pcnt, userrec *user)
{
char oldnick[NICKMAX];
if (!*parameters[0] || !*user->nick)
{
/* We cant put blanks in the parameters, so for this (extremely rare) issue we just put '*' here. */
user->WriteServ("432 %s * :Erroneous Nickname", *user->nick ? user->nick : "*");
return CMD_FAILURE;
}
if (irc::string(user->nick) == irc::string(parameters[0]))
{
/* If its exactly the same, even case, dont do anything. */
if (!strcmp(user->nick,parameters[0]))
return CMD_SUCCESS;
/* Its a change of case. People insisted that they should be
* able to do silly things like this even though the RFC says
* the nick AAA is the same as the nick aaa.
*/
strlcpy(oldnick, user->nick, NICKMAX - 1);
int MOD_RESULT = 0;
FOREACH_RESULT(I_OnUserPreNick,OnUserPreNick(user,parameters[0]));
if (MOD_RESULT)
return CMD_FAILURE;
if (user->registered == REG_ALL)
user->WriteCommon("NICK %s",parameters[0]);
strlcpy(user->nick, parameters[0], NICKMAX - 1);
user->InvalidateCache();
FOREACH_MOD(I_OnUserPostNick,OnUserPostNick(user,oldnick));
return CMD_SUCCESS;
}
else
{
QLine* mq = ServerInstance->XLines->matches_qline(parameters[0]);
if (mq)
{
ServerInstance->SNO->WriteToSnoMask('x', "Q-Lined nickname %s from %s!%s@%s: %s", parameters[0], user->nick, user->ident, user->host, mq->reason);
user->WriteServ("432 %s %s :Invalid nickname: %s",user->nick,parameters[0], mq->reason);
return CMD_FAILURE;
}
/* Check for nickname overruled -
* This happens when one user has connected and sent only NICK, and is essentially
* "camping" upon a nickname. To give the new user connecting a fair chance of having
* the nickname too, we force a nickchange on the older user (Simply the one who was
* here first, no TS checks need to take place here)
*/
userrec* InUse = ServerInstance->FindNick(parameters[0]);
if (InUse && (InUse != user) && (ServerInstance->IsNick(parameters[0])))
{
if (InUse->registered != REG_ALL)
{
/* change the nick of the older user to nnn-overruled,
* where nnn is their file descriptor. We know this to be unique.
* NOTE: We must do this and not quit the user, even though we do
* not have UID support yet. This is because if we set this user
* as quitting and then introduce the new user before the old one
* has quit, then the user hash gets totally buggered.
* (Yes, that is a technical term). -- Brain
*/
std::string changeback = ConvToStr(InUse->GetFd()) + "-overruled";
InUse->WriteTo(InUse, "NICK %s", changeback.c_str());
InUse->WriteServ("433 %s %s :Nickname overruled.", InUse->nick, InUse->nick);
InUse->UpdateNickHash(changeback.c_str());
strlcpy(InUse->nick, changeback.c_str(), NICKMAX - 1);
InUse->InvalidateCache();
/* Take away their nickname-sent state forcing them to send a nick again */
InUse->registered &= ~REG_NICK;
}
else
{
user->WriteServ("433 %s %s :Nickname is already in use.", user->registered >= REG_NICK ? user->nick : "*", parameters[0]);
return CMD_FAILURE;
}
}
}
if ((!ServerInstance->IsNick(parameters[0])) && (IS_LOCAL(user)))
{
user->WriteServ("432 %s %s :Erroneous Nickname",user->nick,parameters[0]);
return CMD_FAILURE;
}
if (user->registered == REG_ALL)
{
int MOD_RESULT = 0;
FOREACH_RESULT(I_OnUserPreNick,OnUserPreNick(user,parameters[0]));
if (MOD_RESULT) {
// if a module returns true, the nick change is silently forbidden.
return CMD_FAILURE;
}
user->WriteCommon("NICK %s",parameters[0]);
}
strlcpy(oldnick, user->nick, NICKMAX - 1);
/* change the nick of the user in the users_hash */
user = user->UpdateNickHash(parameters[0]);
/* actually change the nick within the record */
if (!user) return CMD_FAILURE;
if (!*user->nick) return CMD_FAILURE;
strlcpy(user->nick, parameters[0], NICKMAX - 1);
user->InvalidateCache();
/* Update display nicks */
for (UCListIter v = user->chans.begin(); v != user->chans.end(); v++)
{
CUList* ulist = v->first->GetUsers();
CUList::iterator i = ulist->find(user);
if (i != ulist->end())
i->second = user->nick;
}
if (user->registered < REG_NICKUSER)
{
user->registered = (user->registered | REG_NICK);
if (ServerInstance->Config->NoUserDns)
{
user->dns_done = true;
ServerInstance->next_call = ServerInstance->Time();
}
else
{
user->StartDNSLookup();
if (user->dns_done)
{
/* Cached result or instant failure - fall right through if possible */
ServerInstance->next_call = ServerInstance->Time();
}
else
{
if (ServerInstance->next_call > ServerInstance->Time() + ServerInstance->Config->dns_timeout)
ServerInstance->next_call = ServerInstance->Time() + ServerInstance->Config->dns_timeout;
}
}
}
if (user->registered == REG_NICKUSER)
{
/* user is registered now, bit 0 = USER command, bit 1 = sent a NICK command */
int MOD_RESULT = 0;
FOREACH_RESULT(I_OnUserRegister,OnUserRegister(user));
if (MOD_RESULT > 0)
return CMD_FAILURE;
}
if (user->registered == REG_ALL)
{
FOREACH_MOD(I_OnUserPostNick,OnUserPostNick(user,oldnick));
}
return CMD_SUCCESS;
}
\ No newline at end of file +/* +------------------------------------+ + * | 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 "users.h" +#include "modules.h" +#include "xline.h" +#include "commands/cmd_nick.h" + +extern "C" DllExport command_t* init_command(InspIRCd* Instance) +{ + return new cmd_nick(Instance); +} + +/** Handle nick changes from users. + * NOTE: If you are used to ircds based on ircd2.8, and are looking + * for the client introduction code in here, youre in the wrong place. + * You need to look in the spanningtree module for this! + */ +CmdResult cmd_nick::Handle (const char** parameters, int pcnt, userrec *user) +{ + char oldnick[NICKMAX]; + + if (!*parameters[0] || !*user->nick) + { + /* We cant put blanks in the parameters, so for this (extremely rare) issue we just put '*' here. */ + user->WriteServ("432 %s * :Erroneous Nickname", *user->nick ? user->nick : "*"); + return CMD_FAILURE; + } + + if (irc::string(user->nick) == irc::string(parameters[0])) + { + /* If its exactly the same, even case, dont do anything. */ + if (!strcmp(user->nick,parameters[0])) + return CMD_SUCCESS; + + /* Its a change of case. People insisted that they should be + * able to do silly things like this even though the RFC says + * the nick AAA is the same as the nick aaa. + */ + strlcpy(oldnick, user->nick, NICKMAX - 1); + int MOD_RESULT = 0; + FOREACH_RESULT(I_OnUserPreNick,OnUserPreNick(user,parameters[0])); + if (MOD_RESULT) + return CMD_FAILURE; + if (user->registered == REG_ALL) + user->WriteCommon("NICK %s",parameters[0]); + strlcpy(user->nick, parameters[0], NICKMAX - 1); + user->InvalidateCache(); + FOREACH_MOD(I_OnUserPostNick,OnUserPostNick(user,oldnick)); + return CMD_SUCCESS; + } + else + { + QLine* mq = ServerInstance->XLines->matches_qline(parameters[0]); + if (mq) + { + ServerInstance->SNO->WriteToSnoMask('x', "Q-Lined nickname %s from %s!%s@%s: %s", parameters[0], user->nick, user->ident, user->host, mq->reason); + user->WriteServ("432 %s %s :Invalid nickname: %s",user->nick,parameters[0], mq->reason); + return CMD_FAILURE; + } + /* Check for nickname overruled - + * This happens when one user has connected and sent only NICK, and is essentially + * "camping" upon a nickname. To give the new user connecting a fair chance of having + * the nickname too, we force a nickchange on the older user (Simply the one who was + * here first, no TS checks need to take place here) + */ + userrec* InUse = ServerInstance->FindNick(parameters[0]); + if (InUse && (InUse != user) && (ServerInstance->IsNick(parameters[0]))) + { + if (InUse->registered != REG_ALL) + { + /* change the nick of the older user to nnn-overruled, + * where nnn is their file descriptor. We know this to be unique. + * NOTE: We must do this and not quit the user, even though we do + * not have UID support yet. This is because if we set this user + * as quitting and then introduce the new user before the old one + * has quit, then the user hash gets totally buggered. + * (Yes, that is a technical term). -- Brain + */ + std::string changeback = ConvToStr(InUse->GetFd()) + "-overruled"; + InUse->WriteTo(InUse, "NICK %s", changeback.c_str()); + InUse->WriteServ("433 %s %s :Nickname overruled.", InUse->nick, InUse->nick); + InUse->UpdateNickHash(changeback.c_str()); + strlcpy(InUse->nick, changeback.c_str(), NICKMAX - 1); + InUse->InvalidateCache(); + /* Take away their nickname-sent state forcing them to send a nick again */ + InUse->registered &= ~REG_NICK; + } + else + { + user->WriteServ("433 %s %s :Nickname is already in use.", user->registered >= REG_NICK ? user->nick : "*", parameters[0]); + return CMD_FAILURE; + } + } + } + if ((!ServerInstance->IsNick(parameters[0])) && (IS_LOCAL(user))) + { + user->WriteServ("432 %s %s :Erroneous Nickname",user->nick,parameters[0]); + return CMD_FAILURE; + } + + if (user->registered == REG_ALL) + { + int MOD_RESULT = 0; + FOREACH_RESULT(I_OnUserPreNick,OnUserPreNick(user,parameters[0])); + if (MOD_RESULT) { + // if a module returns true, the nick change is silently forbidden. + return CMD_FAILURE; + } + + user->WriteCommon("NICK %s",parameters[0]); + + } + + strlcpy(oldnick, user->nick, NICKMAX - 1); + + /* change the nick of the user in the users_hash */ + user = user->UpdateNickHash(parameters[0]); + + /* actually change the nick within the record */ + if (!user) return CMD_FAILURE; + if (!*user->nick) return CMD_FAILURE; + + strlcpy(user->nick, parameters[0], NICKMAX - 1); + + user->InvalidateCache(); + + /* Update display nicks */ + for (UCListIter v = user->chans.begin(); v != user->chans.end(); v++) + { + CUList* ulist = v->first->GetUsers(); + CUList::iterator i = ulist->find(user); + if (i != ulist->end()) + i->second = user->nick; + } + + if (user->registered < REG_NICKUSER) + { + user->registered = (user->registered | REG_NICK); + + if (ServerInstance->Config->NoUserDns) + { + user->dns_done = true; + ServerInstance->next_call = ServerInstance->Time(); + } + else + { + user->StartDNSLookup(); + if (user->dns_done) + { + /* Cached result or instant failure - fall right through if possible */ + ServerInstance->next_call = ServerInstance->Time(); + } + else + { + if (ServerInstance->next_call > ServerInstance->Time() + ServerInstance->Config->dns_timeout) + ServerInstance->next_call = ServerInstance->Time() + ServerInstance->Config->dns_timeout; + } + } + } + if (user->registered == REG_NICKUSER) + { + /* user is registered now, bit 0 = USER command, bit 1 = sent a NICK command */ + int MOD_RESULT = 0; + FOREACH_RESULT(I_OnUserRegister,OnUserRegister(user)); + if (MOD_RESULT > 0) + return CMD_FAILURE; + } + if (user->registered == REG_ALL) + { + FOREACH_MOD(I_OnUserPostNick,OnUserPostNick(user,oldnick)); + } + + return CMD_SUCCESS; + +} + diff --git a/src/cmd_notice.cpp b/src/cmd_notice.cpp index 27fdd5279..a797fefab 100644 --- a/src/cmd_notice.cpp +++ b/src/cmd_notice.cpp @@ -1 +1,158 @@ -/* +------------------------------------+
* | 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 "users.h"
#include "modules.h"
#include "wildcard.h"
#include "commands/cmd_notice.h"
extern "C" DllExport command_t* init_command(InspIRCd* Instance)
{
return new cmd_notice(Instance);
}
CmdResult cmd_notice::Handle (const char** parameters, int pcnt, userrec *user)
{
userrec *dest;
chanrec *chan;
CUList exempt_list;
user->idle_lastmsg = ServerInstance->Time();
if (ServerInstance->Parser->LoopCall(user, this, parameters, pcnt, 0))
return CMD_SUCCESS;
if ((parameters[0][0] == '$') && (IS_OPER(user) || ServerInstance->ULine(user->server)))
{
int MOD_RESULT = 0;
std::string temp = parameters[1];
FOREACH_RESULT(I_OnUserPreNotice,OnUserPreNotice(user,(void*)parameters[0],TYPE_SERVER,temp,0,exempt_list));
if (MOD_RESULT)
return CMD_FAILURE;
parameters[1] = temp.c_str();
// notice to server mask
const char* servermask = parameters[0] + 1;
if (match(ServerInstance->Config->ServerName,servermask))
{
user->SendAll("NOTICE", "%s", parameters[1]);
}
FOREACH_MOD(I_OnUserNotice,OnUserNotice(user,(void*)parameters[0],TYPE_SERVER,parameters[1],0,exempt_list));
return CMD_SUCCESS;
}
char status = 0;
if ((*parameters[0] == '@') || (*parameters[0] == '%') || (*parameters[0] == '+'))
{
status = *parameters[0];
parameters[0]++;
}
if (*parameters[0] == '#')
{
chan = ServerInstance->FindChan(parameters[0]);
exempt_list[user] = user->nick;
if (chan)
{
if (IS_LOCAL(user))
{
if ((chan->modes[CM_NOEXTERNAL]) && (!chan->HasUser(user)))
{
user->WriteServ("404 %s %s :Cannot send to channel (no external messages)", user->nick, chan->name);
return CMD_FAILURE;
}
if ((chan->modes[CM_MODERATED]) && (chan->GetStatus(user) < STATUS_VOICE))
{
user->WriteServ("404 %s %s :Cannot send to channel (+m)", user->nick, chan->name);
return CMD_FAILURE;
}
}
int MOD_RESULT = 0;
std::string temp = parameters[1];
FOREACH_RESULT(I_OnUserPreNotice,OnUserPreNotice(user,chan,TYPE_CHANNEL,temp,status, exempt_list));
if (MOD_RESULT) {
return CMD_FAILURE;
}
parameters[1] = temp.c_str();
if (temp.empty())
{
user->WriteServ("412 %s :No text to send", user->nick);
return CMD_FAILURE;
}
if (status)
{
if (ServerInstance->Config->UndernetMsgPrefix)
{
chan->WriteAllExcept(user, false, status, exempt_list, "NOTICE %c%s :%c %s", status, chan->name, status, parameters[1]);
}
else
{
chan->WriteAllExcept(user, false, status, exempt_list, "NOTICE %c%s :%s", status, chan->name, parameters[1]);
}
}
else
{
chan->WriteAllExcept(user, false, status, exempt_list, "NOTICE %s :%s", chan->name, parameters[1]);
}
FOREACH_MOD(I_OnUserNotice,OnUserNotice(user,chan,TYPE_CHANNEL,parameters[1],status,exempt_list));
}
else
{
/* no such nick/channel */
user->WriteServ("401 %s %s :No such nick/channel",user->nick, parameters[0]);
return CMD_FAILURE;
}
return CMD_SUCCESS;
}
dest = ServerInstance->FindNick(parameters[0]);
if (dest)
{
if (!*parameters[1])
{
user->WriteServ("412 %s :No text to send", user->nick);
return CMD_FAILURE;
}
int MOD_RESULT = 0;
std::string temp = parameters[1];
FOREACH_RESULT(I_OnUserPreNotice,OnUserPreNotice(user,dest,TYPE_USER,temp,0,exempt_list));
if (MOD_RESULT) {
return CMD_FAILURE;
}
parameters[1] = (char*)temp.c_str();
if (IS_LOCAL(dest))
{
// direct write, same server
user->WriteTo(dest, "NOTICE %s :%s", dest->nick, parameters[1]);
}
FOREACH_MOD(I_OnUserNotice,OnUserNotice(user,dest,TYPE_USER,parameters[1],0,exempt_list));
}
else
{
/* no such nick/channel */
user->WriteServ("401 %s %s :No such nick/channel",user->nick, parameters[0]);
return CMD_FAILURE;
}
return CMD_SUCCESS;
}
\ No newline at end of file +/* +------------------------------------+ + * | 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 "users.h" +#include "modules.h" +#include "wildcard.h" +#include "commands/cmd_notice.h" + +extern "C" DllExport command_t* init_command(InspIRCd* Instance) +{ + return new cmd_notice(Instance); +} + +CmdResult cmd_notice::Handle (const char** parameters, int pcnt, userrec *user) +{ + userrec *dest; + chanrec *chan; + + CUList exempt_list; + + user->idle_lastmsg = ServerInstance->Time(); + + if (ServerInstance->Parser->LoopCall(user, this, parameters, pcnt, 0)) + return CMD_SUCCESS; + if ((parameters[0][0] == '$') && (IS_OPER(user) || ServerInstance->ULine(user->server))) + { + int MOD_RESULT = 0; + std::string temp = parameters[1]; + FOREACH_RESULT(I_OnUserPreNotice,OnUserPreNotice(user,(void*)parameters[0],TYPE_SERVER,temp,0,exempt_list)); + if (MOD_RESULT) + return CMD_FAILURE; + parameters[1] = temp.c_str(); + // notice to server mask + const char* servermask = parameters[0] + 1; + if (match(ServerInstance->Config->ServerName,servermask)) + { + user->SendAll("NOTICE", "%s", parameters[1]); + } + FOREACH_MOD(I_OnUserNotice,OnUserNotice(user,(void*)parameters[0],TYPE_SERVER,parameters[1],0,exempt_list)); + return CMD_SUCCESS; + } + char status = 0; + if ((*parameters[0] == '@') || (*parameters[0] == '%') || (*parameters[0] == '+')) + { + status = *parameters[0]; + parameters[0]++; + } + if (*parameters[0] == '#') + { + chan = ServerInstance->FindChan(parameters[0]); + + exempt_list[user] = user->nick; + + if (chan) + { + if (IS_LOCAL(user)) + { + if ((chan->modes[CM_NOEXTERNAL]) && (!chan->HasUser(user))) + { + user->WriteServ("404 %s %s :Cannot send to channel (no external messages)", user->nick, chan->name); + return CMD_FAILURE; + } + if ((chan->modes[CM_MODERATED]) && (chan->GetStatus(user) < STATUS_VOICE)) + { + user->WriteServ("404 %s %s :Cannot send to channel (+m)", user->nick, chan->name); + return CMD_FAILURE; + } + } + int MOD_RESULT = 0; + + std::string temp = parameters[1]; + FOREACH_RESULT(I_OnUserPreNotice,OnUserPreNotice(user,chan,TYPE_CHANNEL,temp,status, exempt_list)); + if (MOD_RESULT) { + return CMD_FAILURE; + } + parameters[1] = temp.c_str(); + + if (temp.empty()) + { + user->WriteServ("412 %s :No text to send", user->nick); + return CMD_FAILURE; + } + + if (status) + { + if (ServerInstance->Config->UndernetMsgPrefix) + { + chan->WriteAllExcept(user, false, status, exempt_list, "NOTICE %c%s :%c %s", status, chan->name, status, parameters[1]); + } + else + { + chan->WriteAllExcept(user, false, status, exempt_list, "NOTICE %c%s :%s", status, chan->name, parameters[1]); + } + } + else + { + chan->WriteAllExcept(user, false, status, exempt_list, "NOTICE %s :%s", chan->name, parameters[1]); + } + + FOREACH_MOD(I_OnUserNotice,OnUserNotice(user,chan,TYPE_CHANNEL,parameters[1],status,exempt_list)); + } + else + { + /* no such nick/channel */ + user->WriteServ("401 %s %s :No such nick/channel",user->nick, parameters[0]); + return CMD_FAILURE; + } + return CMD_SUCCESS; + } + + dest = ServerInstance->FindNick(parameters[0]); + if (dest) + { + if (!*parameters[1]) + { + user->WriteServ("412 %s :No text to send", user->nick); + return CMD_FAILURE; + } + + int MOD_RESULT = 0; + std::string temp = parameters[1]; + FOREACH_RESULT(I_OnUserPreNotice,OnUserPreNotice(user,dest,TYPE_USER,temp,0,exempt_list)); + if (MOD_RESULT) { + return CMD_FAILURE; + } + parameters[1] = (char*)temp.c_str(); + + if (IS_LOCAL(dest)) + { + // direct write, same server + user->WriteTo(dest, "NOTICE %s :%s", dest->nick, parameters[1]); + } + + FOREACH_MOD(I_OnUserNotice,OnUserNotice(user,dest,TYPE_USER,parameters[1],0,exempt_list)); + } + else + { + /* no such nick/channel */ + user->WriteServ("401 %s %s :No such nick/channel",user->nick, parameters[0]); + return CMD_FAILURE; + } + + return CMD_SUCCESS; + +} + diff --git a/src/cmd_oper.cpp b/src/cmd_oper.cpp index af811115d..686182876 100644 --- a/src/cmd_oper.cpp +++ b/src/cmd_oper.cpp @@ -1 +1,153 @@ -/* +------------------------------------+
* | 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 "typedefs.h"
#include "users.h"
#include "modules.h"
#include "wildcard.h"
#include "commands/cmd_oper.h"
#include "hashcomp.h"
bool OneOfMatches(const char* host, const char* ip, const char* hostlist)
{
std::stringstream hl(hostlist);
std::string xhost;
while (hl >> xhost)
{
if (match(host,xhost.c_str()) || match(ip,xhost.c_str(),true))
{
return true;
}
}
return false;
}
extern "C" DllExport command_t* init_command(InspIRCd* Instance)
{
return new cmd_oper(Instance);
}
CmdResult cmd_oper::Handle (const char** parameters, int pcnt, userrec *user)
{
char LoginName[MAXBUF];
char Password[MAXBUF];
char OperType[MAXBUF];
char TypeName[MAXBUF];
char HostName[MAXBUF];
char TheHost[MAXBUF];
char TheIP[MAXBUF];
int j;
bool found = false;
bool type_invalid = false;
bool match_login = false;
bool match_pass = false;
bool match_hosts = false;
snprintf(TheHost,MAXBUF,"%s@%s",user->ident,user->host);
snprintf(TheIP, MAXBUF,"%s@%s",user->ident,user->GetIPString());
for (int i = 0; i < ServerInstance->Config->ConfValueEnum(ServerInstance->Config->config_data, "oper"); i++)
{
ServerInstance->Config->ConfValue(ServerInstance->Config->config_data, "oper", "name", i, LoginName, MAXBUF);
ServerInstance->Config->ConfValue(ServerInstance->Config->config_data, "oper", "password", i, Password, MAXBUF);
ServerInstance->Config->ConfValue(ServerInstance->Config->config_data, "oper", "type", i, OperType, MAXBUF);
ServerInstance->Config->ConfValue(ServerInstance->Config->config_data, "oper", "host", i, HostName, MAXBUF);
match_login = !strcmp(LoginName,parameters[0]);
match_pass = !ServerInstance->OperPassCompare(Password,parameters[1], i);
match_hosts = OneOfMatches(TheHost,TheIP,HostName);
if (match_login && match_pass && match_hosts)
{
type_invalid = true;
for (j =0; j < ServerInstance->Config->ConfValueEnum(ServerInstance->Config->config_data, "type"); j++)
{
ServerInstance->Config->ConfValue(ServerInstance->Config->config_data, "type","name", j, TypeName, MAXBUF);
if (!strcmp(TypeName,OperType))
{
/* found this oper's opertype */
if (!ServerInstance->IsNick(TypeName))
{
user->WriteServ("491 %s :Invalid oper type (oper types must follow the same syntax as nicknames)",user->nick);
ServerInstance->SNO->WriteToSnoMask('o',"CONFIGURATION ERROR! Oper type '%s' contains invalid characters",OperType);
ServerInstance->Log(DEFAULT,"OPER: Failed oper attempt by %s!%s@%s: credentials valid, but oper type erroneous.",user->nick,user->ident,user->host);
return CMD_FAILURE;
}
ServerInstance->Config->ConfValue(ServerInstance->Config->config_data, "type","host", j, HostName, MAXBUF);
if (*HostName)
user->ChangeDisplayedHost(HostName);
found = true;
type_invalid = false;
break;
}
}
}
if (match_login || found)
break;
}
if (found)
{
/* correct oper credentials */
ServerInstance->SNO->WriteToSnoMask('o',"%s (%s@%s) is now an IRC operator of type %s (using oper '%s')",user->nick,user->ident,user->host,irc::Spacify(OperType),parameters[0]);
user->WriteServ("381 %s :You are now an IRC operator of type %s",user->nick,irc::Spacify(OperType));
if (!user->modes[UM_OPERATOR])
user->Oper(OperType);
}
else
{
std::deque<std::string> n;
n.push_back("o");
char broadcast[MAXBUF];
if (!type_invalid)
{
std::string fields;
if (!match_login)
fields.append("login ");
if (!match_pass)
fields.append("password ");
if (!match_hosts)
fields.append("hosts");
user->WriteServ("491 %s :Invalid oper credentials",user->nick);
snprintf(broadcast, MAXBUF, "WARNING! Failed oper attempt by %s!%s@%s using login '%s': The following fields do not match: %s",user->nick,user->ident,user->host, parameters[0], fields.c_str());
ServerInstance->SNO->WriteToSnoMask('o',std::string(broadcast));
n.push_back(broadcast);
Event rmode2((char *)&n, NULL, "send_snoset");
rmode2.Send(ServerInstance);
ServerInstance->Log(DEFAULT,"OPER: Failed oper attempt by %s!%s@%s using login '%s': The following fields did not match: %s",user->nick,user->ident,user->host,parameters[0],fields.c_str());
return CMD_FAILURE;
}
else
{
user->WriteServ("491 %s :Your oper block does not have a valid opertype associated with it",user->nick);
snprintf(broadcast, MAXBUF, "CONFIGURATION ERROR! Oper block '%s': missing OperType %s",parameters[0],OperType);
ServerInstance->SNO->WriteToSnoMask('o', std::string(broadcast));
n.push_back(broadcast);
Event rmode2((char *)&n, NULL, "send_snoset");
rmode2.Send(ServerInstance);
ServerInstance->Log(DEFAULT,"OPER: Failed oper attempt by %s!%s@%s using login '%s': credentials valid, but oper type nonexistent.",user->nick,user->ident,user->host,parameters[0]);
return CMD_FAILURE;
}
}
return CMD_SUCCESS;
}
\ No newline at end of file +/* +------------------------------------+ + * | 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 "typedefs.h" +#include "users.h" +#include "modules.h" +#include "wildcard.h" +#include "commands/cmd_oper.h" +#include "hashcomp.h" + +bool OneOfMatches(const char* host, const char* ip, const char* hostlist) +{ + std::stringstream hl(hostlist); + std::string xhost; + while (hl >> xhost) + { + if (match(host,xhost.c_str()) || match(ip,xhost.c_str(),true)) + { + return true; + } + } + return false; +} + +extern "C" DllExport command_t* init_command(InspIRCd* Instance) +{ + return new cmd_oper(Instance); +} + +CmdResult cmd_oper::Handle (const char** parameters, int pcnt, userrec *user) +{ + char LoginName[MAXBUF]; + char Password[MAXBUF]; + char OperType[MAXBUF]; + char TypeName[MAXBUF]; + char HostName[MAXBUF]; + char TheHost[MAXBUF]; + char TheIP[MAXBUF]; + int j; + bool found = false; + bool type_invalid = false; + + bool match_login = false; + bool match_pass = false; + bool match_hosts = false; + + snprintf(TheHost,MAXBUF,"%s@%s",user->ident,user->host); + snprintf(TheIP, MAXBUF,"%s@%s",user->ident,user->GetIPString()); + + for (int i = 0; i < ServerInstance->Config->ConfValueEnum(ServerInstance->Config->config_data, "oper"); i++) + { + ServerInstance->Config->ConfValue(ServerInstance->Config->config_data, "oper", "name", i, LoginName, MAXBUF); + ServerInstance->Config->ConfValue(ServerInstance->Config->config_data, "oper", "password", i, Password, MAXBUF); + ServerInstance->Config->ConfValue(ServerInstance->Config->config_data, "oper", "type", i, OperType, MAXBUF); + ServerInstance->Config->ConfValue(ServerInstance->Config->config_data, "oper", "host", i, HostName, MAXBUF); + + match_login = !strcmp(LoginName,parameters[0]); + match_pass = !ServerInstance->OperPassCompare(Password,parameters[1], i); + match_hosts = OneOfMatches(TheHost,TheIP,HostName); + + if (match_login && match_pass && match_hosts) + { + type_invalid = true; + for (j =0; j < ServerInstance->Config->ConfValueEnum(ServerInstance->Config->config_data, "type"); j++) + { + ServerInstance->Config->ConfValue(ServerInstance->Config->config_data, "type","name", j, TypeName, MAXBUF); + + if (!strcmp(TypeName,OperType)) + { + /* found this oper's opertype */ + if (!ServerInstance->IsNick(TypeName)) + { + user->WriteServ("491 %s :Invalid oper type (oper types must follow the same syntax as nicknames)",user->nick); + ServerInstance->SNO->WriteToSnoMask('o',"CONFIGURATION ERROR! Oper type '%s' contains invalid characters",OperType); + ServerInstance->Log(DEFAULT,"OPER: Failed oper attempt by %s!%s@%s: credentials valid, but oper type erroneous.",user->nick,user->ident,user->host); + return CMD_FAILURE; + } + ServerInstance->Config->ConfValue(ServerInstance->Config->config_data, "type","host", j, HostName, MAXBUF); + if (*HostName) + user->ChangeDisplayedHost(HostName); + found = true; + type_invalid = false; + break; + } + } + } + if (match_login || found) + break; + } + if (found) + { + /* correct oper credentials */ + ServerInstance->SNO->WriteToSnoMask('o',"%s (%s@%s) is now an IRC operator of type %s (using oper '%s')",user->nick,user->ident,user->host,irc::Spacify(OperType),parameters[0]); + user->WriteServ("381 %s :You are now an IRC operator of type %s",user->nick,irc::Spacify(OperType)); + if (!user->modes[UM_OPERATOR]) + user->Oper(OperType); + } + else + { + std::deque<std::string> n; + n.push_back("o"); + char broadcast[MAXBUF]; + + if (!type_invalid) + { + std::string fields; + if (!match_login) + fields.append("login "); + if (!match_pass) + fields.append("password "); + if (!match_hosts) + fields.append("hosts"); + user->WriteServ("491 %s :Invalid oper credentials",user->nick); + + snprintf(broadcast, MAXBUF, "WARNING! Failed oper attempt by %s!%s@%s using login '%s': The following fields do not match: %s",user->nick,user->ident,user->host, parameters[0], fields.c_str()); + ServerInstance->SNO->WriteToSnoMask('o',std::string(broadcast)); + n.push_back(broadcast); + Event rmode2((char *)&n, NULL, "send_snoset"); + rmode2.Send(ServerInstance); + + ServerInstance->Log(DEFAULT,"OPER: Failed oper attempt by %s!%s@%s using login '%s': The following fields did not match: %s",user->nick,user->ident,user->host,parameters[0],fields.c_str()); + return CMD_FAILURE; + } + else + { + user->WriteServ("491 %s :Your oper block does not have a valid opertype associated with it",user->nick); + + snprintf(broadcast, MAXBUF, "CONFIGURATION ERROR! Oper block '%s': missing OperType %s",parameters[0],OperType); + + ServerInstance->SNO->WriteToSnoMask('o', std::string(broadcast)); + n.push_back(broadcast); + Event rmode2((char *)&n, NULL, "send_snoset"); + rmode2.Send(ServerInstance); + + ServerInstance->Log(DEFAULT,"OPER: Failed oper attempt by %s!%s@%s using login '%s': credentials valid, but oper type nonexistent.",user->nick,user->ident,user->host,parameters[0]); + return CMD_FAILURE; + } + } + return CMD_SUCCESS; +} + diff --git a/src/cmd_part.cpp b/src/cmd_part.cpp index 3bf22ac5d..347466c22 100644 --- a/src/cmd_part.cpp +++ b/src/cmd_part.cpp @@ -1 +1,43 @@ -/* +------------------------------------+
* | 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 "users.h"
#include "commands/cmd_part.h"
extern "C" DllExport command_t* init_command(InspIRCd* Instance)
{
return new cmd_part(Instance);
}
CmdResult cmd_part::Handle (const char** parameters, int pcnt, userrec *user)
{
if (ServerInstance->Parser->LoopCall(user, this, parameters, pcnt, 0))
return CMD_SUCCESS;
chanrec* c = ServerInstance->FindChan(parameters[0]);
if (c)
{
if (!c->PartUser(user, pcnt > 1 ? parameters[1] : NULL))
/* Arse, who stole our channel! :/ */
delete c;
}
else
{
user->WriteServ( "401 %s %s :No such channel", user->nick, parameters[0]);
return CMD_FAILURE;
}
return CMD_SUCCESS;
}
\ No newline at end of file +/* +------------------------------------+ + * | 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 "users.h" +#include "commands/cmd_part.h" + +extern "C" DllExport command_t* init_command(InspIRCd* Instance) +{ + return new cmd_part(Instance); +} + +CmdResult cmd_part::Handle (const char** parameters, int pcnt, userrec *user) +{ + if (ServerInstance->Parser->LoopCall(user, this, parameters, pcnt, 0)) + return CMD_SUCCESS; + + chanrec* c = ServerInstance->FindChan(parameters[0]); + + if (c) + { + if (!c->PartUser(user, pcnt > 1 ? parameters[1] : NULL)) + /* Arse, who stole our channel! :/ */ + delete c; + } + else + { + user->WriteServ( "401 %s %s :No such channel", user->nick, parameters[0]); + return CMD_FAILURE; + } + + return CMD_SUCCESS; +} diff --git a/src/cmd_pass.cpp b/src/cmd_pass.cpp index 0a836ccdf..29c83c3bf 100644 --- a/src/cmd_pass.cpp +++ b/src/cmd_pass.cpp @@ -1 +1,42 @@ -/* +------------------------------------+
* | 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 "users.h"
#include "commands/cmd_pass.h"
extern "C" DllExport command_t* init_command(InspIRCd* Instance)
{
return new cmd_pass(Instance);
}
CmdResult cmd_pass::Handle (const char** parameters, int pcnt, userrec *user)
{
// Check to make sure they havnt registered -- Fix by FCS
if (user->registered == REG_ALL)
{
user->WriteServ("462 %s :You may not reregister",user->nick);
return CMD_FAILURE;
}
ConnectClass* a = user->GetClass();
if (!a)
return CMD_FAILURE;
strlcpy(user->password,parameters[0],63);
if (a->GetPass() == parameters[0])
{
user->haspassed = true;
}
return CMD_SUCCESS;
}
\ No newline at end of file +/* +------------------------------------+ + * | 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 "users.h" +#include "commands/cmd_pass.h" + +extern "C" DllExport command_t* init_command(InspIRCd* Instance) +{ + return new cmd_pass(Instance); +} + +CmdResult cmd_pass::Handle (const char** parameters, int pcnt, userrec *user) +{ + // Check to make sure they havnt registered -- Fix by FCS + if (user->registered == REG_ALL) + { + user->WriteServ("462 %s :You may not reregister",user->nick); + return CMD_FAILURE; + } + ConnectClass* a = user->GetClass(); + if (!a) + return CMD_FAILURE; + + strlcpy(user->password,parameters[0],63); + if (a->GetPass() == parameters[0]) + { + user->haspassed = true; + } + + return CMD_SUCCESS; +} diff --git a/src/cmd_ping.cpp b/src/cmd_ping.cpp index c36415d12..afb708d70 100644 --- a/src/cmd_ping.cpp +++ b/src/cmd_ping.cpp @@ -1 +1,28 @@ -/* +------------------------------------+
* | 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 "users.h"
#include "commands/cmd_ping.h"
extern "C" DllExport command_t* init_command(InspIRCd* Instance)
{
return new cmd_ping(Instance);
}
CmdResult cmd_ping::Handle (const char** parameters, int pcnt, userrec *user)
{
user->WriteServ("PONG %s :%s",ServerInstance->Config->ServerName,parameters[0]);
return CMD_SUCCESS;
}
\ No newline at end of file +/* +------------------------------------+ + * | 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 "users.h" +#include "commands/cmd_ping.h" + +extern "C" DllExport command_t* init_command(InspIRCd* Instance) +{ + return new cmd_ping(Instance); +} + +CmdResult cmd_ping::Handle (const char** parameters, int pcnt, userrec *user) +{ + user->WriteServ("PONG %s :%s",ServerInstance->Config->ServerName,parameters[0]); + return CMD_SUCCESS; +} diff --git a/src/cmd_pong.cpp b/src/cmd_pong.cpp index 16b42355b..c89542240 100644 --- a/src/cmd_pong.cpp +++ b/src/cmd_pong.cpp @@ -1 +1,28 @@ -/* +------------------------------------+
* | 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 "users.h"
#include "commands/cmd_pong.h"
extern "C" DllExport command_t* init_command(InspIRCd* Instance)
{
return new cmd_pong(Instance);
}
CmdResult cmd_pong::Handle (const char** parameters, int pcnt, userrec *user)
{
// set the user as alive so they survive to next ping
user->lastping = 1;
return CMD_SUCCESS;
}
\ No newline at end of file +/* +------------------------------------+ + * | 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 "users.h" +#include "commands/cmd_pong.h" + +extern "C" DllExport command_t* init_command(InspIRCd* Instance) +{ + return new cmd_pong(Instance); +} + +CmdResult cmd_pong::Handle (const char** parameters, int pcnt, userrec *user) +{ + // set the user as alive so they survive to next ping + user->lastping = 1; + return CMD_SUCCESS; +} diff --git a/src/cmd_privmsg.cpp b/src/cmd_privmsg.cpp index 3fdc12090..f3295df07 100644 --- a/src/cmd_privmsg.cpp +++ b/src/cmd_privmsg.cpp @@ -1 +1,164 @@ -/* +------------------------------------+
* | 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 "users.h"
#include "modules.h"
#include "wildcard.h"
#include "commands/cmd_privmsg.h"
extern "C" DllExport command_t* init_command(InspIRCd* Instance)
{
return new cmd_privmsg(Instance);
}
CmdResult cmd_privmsg::Handle (const char** parameters, int pcnt, userrec *user)
{
userrec *dest;
chanrec *chan;
CUList except_list;
user->idle_lastmsg = ServerInstance->Time();
if (ServerInstance->Parser->LoopCall(user, this, parameters, pcnt, 0))
return CMD_SUCCESS;
if ((parameters[0][0] == '$') && (IS_OPER(user) || ServerInstance->ULine(user->server)))
{
int MOD_RESULT = 0;
std::string temp = parameters[1];
FOREACH_RESULT(I_OnUserPreMessage,OnUserPreMessage(user,(void*)parameters[0],TYPE_SERVER,temp,0,except_list));
if (MOD_RESULT)
return CMD_FAILURE;
parameters[1] = temp.c_str();
// notice to server mask
const char* servermask = parameters[0] + 1;
if (match(ServerInstance->Config->ServerName,servermask))
{
user->SendAll("PRIVMSG", "%s", parameters[1]);
}
FOREACH_MOD(I_OnUserMessage,OnUserMessage(user,(void*)parameters[0],TYPE_SERVER,parameters[1],0,except_list));
return CMD_SUCCESS;
}
char status = 0;
if ((*parameters[0] == '@') || (*parameters[0] == '%') || (*parameters[0] == '+'))
{
status = *parameters[0];
parameters[0]++;
}
if (parameters[0][0] == '#')
{
chan = ServerInstance->FindChan(parameters[0]);
except_list[user] = user->nick;
if (chan)
{
if (IS_LOCAL(user))
{
if ((chan->modes[CM_NOEXTERNAL]) && (!chan->HasUser(user)))
{
user->WriteServ("404 %s %s :Cannot send to channel (no external messages)", user->nick, chan->name);
return CMD_FAILURE;
}
if ((chan->modes[CM_MODERATED]) && (chan->GetStatus(user) < STATUS_VOICE))
{
user->WriteServ("404 %s %s :Cannot send to channel (+m)", user->nick, chan->name);
return CMD_FAILURE;
}
}
int MOD_RESULT = 0;
std::string temp = parameters[1];
FOREACH_RESULT(I_OnUserPreMessage,OnUserPreMessage(user,chan,TYPE_CHANNEL,temp,status,except_list));
if (MOD_RESULT) {
return CMD_FAILURE;
}
parameters[1] = temp.c_str();
/* Check again, a module may have zapped the input string */
if (temp.empty())
{
user->WriteServ("412 %s :No text to send", user->nick);
return CMD_FAILURE;
}
if (status)
{
if (ServerInstance->Config->UndernetMsgPrefix)
{
chan->WriteAllExcept(user, false, status, except_list, "PRIVMSG %c%s :%c %s", status, chan->name, status, parameters[1]);
}
else
{
chan->WriteAllExcept(user, false, status, except_list, "PRIVMSG %c%s :%s", status, chan->name, parameters[1]);
}
}
else
{
chan->WriteAllExcept(user, false, status, except_list, "PRIVMSG %s :%s", chan->name, parameters[1]);
}
FOREACH_MOD(I_OnUserMessage,OnUserMessage(user,chan,TYPE_CHANNEL,parameters[1],status,except_list));
}
else
{
/* no such nick/channel */
user->WriteServ("401 %s %s :No such nick/channel",user->nick, parameters[0]);
return CMD_FAILURE;
}
return CMD_SUCCESS;
}
dest = ServerInstance->FindNick(parameters[0]);
if (dest)
{
if (!*parameters[1])
{
user->WriteServ("412 %s :No text to send", user->nick);
return CMD_FAILURE;
}
if (IS_AWAY(dest))
{
/* auto respond with aweh msg */
user->WriteServ("301 %s %s :%s",user->nick,dest->nick,dest->awaymsg);
}
int MOD_RESULT = 0;
std::string temp = parameters[1];
FOREACH_RESULT(I_OnUserPreMessage,OnUserPreMessage(user,dest,TYPE_USER,temp,0,except_list));
if (MOD_RESULT) {
return CMD_FAILURE;
}
parameters[1] = (char*)temp.c_str();
if (IS_LOCAL(dest))
{
// direct write, same server
user->WriteTo(dest, "PRIVMSG %s :%s", dest->nick, parameters[1]);
}
FOREACH_MOD(I_OnUserMessage,OnUserMessage(user,dest,TYPE_USER,parameters[1],0,except_list));
}
else
{
/* no such nick/channel */
user->WriteServ("401 %s %s :No such nick/channel",user->nick, parameters[0]);
return CMD_FAILURE;
}
return CMD_SUCCESS;
}
\ No newline at end of file +/* +------------------------------------+ + * | 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 "users.h" +#include "modules.h" +#include "wildcard.h" +#include "commands/cmd_privmsg.h" + +extern "C" DllExport command_t* init_command(InspIRCd* Instance) +{ + return new cmd_privmsg(Instance); +} + +CmdResult cmd_privmsg::Handle (const char** parameters, int pcnt, userrec *user) +{ + userrec *dest; + chanrec *chan; + CUList except_list; + + user->idle_lastmsg = ServerInstance->Time(); + + if (ServerInstance->Parser->LoopCall(user, this, parameters, pcnt, 0)) + return CMD_SUCCESS; + + if ((parameters[0][0] == '$') && (IS_OPER(user) || ServerInstance->ULine(user->server))) + { + int MOD_RESULT = 0; + std::string temp = parameters[1]; + FOREACH_RESULT(I_OnUserPreMessage,OnUserPreMessage(user,(void*)parameters[0],TYPE_SERVER,temp,0,except_list)); + if (MOD_RESULT) + return CMD_FAILURE; + parameters[1] = temp.c_str(); + // notice to server mask + const char* servermask = parameters[0] + 1; + if (match(ServerInstance->Config->ServerName,servermask)) + { + user->SendAll("PRIVMSG", "%s", parameters[1]); + } + FOREACH_MOD(I_OnUserMessage,OnUserMessage(user,(void*)parameters[0],TYPE_SERVER,parameters[1],0,except_list)); + return CMD_SUCCESS; + } + char status = 0; + if ((*parameters[0] == '@') || (*parameters[0] == '%') || (*parameters[0] == '+')) + { + status = *parameters[0]; + parameters[0]++; + } + if (parameters[0][0] == '#') + { + chan = ServerInstance->FindChan(parameters[0]); + + except_list[user] = user->nick; + + if (chan) + { + if (IS_LOCAL(user)) + { + if ((chan->modes[CM_NOEXTERNAL]) && (!chan->HasUser(user))) + { + user->WriteServ("404 %s %s :Cannot send to channel (no external messages)", user->nick, chan->name); + return CMD_FAILURE; + } + if ((chan->modes[CM_MODERATED]) && (chan->GetStatus(user) < STATUS_VOICE)) + { + user->WriteServ("404 %s %s :Cannot send to channel (+m)", user->nick, chan->name); + return CMD_FAILURE; + } + } + int MOD_RESULT = 0; + + std::string temp = parameters[1]; + FOREACH_RESULT(I_OnUserPreMessage,OnUserPreMessage(user,chan,TYPE_CHANNEL,temp,status,except_list)); + if (MOD_RESULT) { + return CMD_FAILURE; + } + parameters[1] = temp.c_str(); + + /* Check again, a module may have zapped the input string */ + if (temp.empty()) + { + user->WriteServ("412 %s :No text to send", user->nick); + return CMD_FAILURE; + } + + if (status) + { + if (ServerInstance->Config->UndernetMsgPrefix) + { + chan->WriteAllExcept(user, false, status, except_list, "PRIVMSG %c%s :%c %s", status, chan->name, status, parameters[1]); + } + else + { + chan->WriteAllExcept(user, false, status, except_list, "PRIVMSG %c%s :%s", status, chan->name, parameters[1]); + } + } + else + { + chan->WriteAllExcept(user, false, status, except_list, "PRIVMSG %s :%s", chan->name, parameters[1]); + } + + FOREACH_MOD(I_OnUserMessage,OnUserMessage(user,chan,TYPE_CHANNEL,parameters[1],status,except_list)); + } + else + { + /* no such nick/channel */ + user->WriteServ("401 %s %s :No such nick/channel",user->nick, parameters[0]); + return CMD_FAILURE; + } + return CMD_SUCCESS; + } + + dest = ServerInstance->FindNick(parameters[0]); + if (dest) + { + if (!*parameters[1]) + { + user->WriteServ("412 %s :No text to send", user->nick); + return CMD_FAILURE; + } + + if (IS_AWAY(dest)) + { + /* auto respond with aweh msg */ + user->WriteServ("301 %s %s :%s",user->nick,dest->nick,dest->awaymsg); + } + + int MOD_RESULT = 0; + + std::string temp = parameters[1]; + FOREACH_RESULT(I_OnUserPreMessage,OnUserPreMessage(user,dest,TYPE_USER,temp,0,except_list)); + if (MOD_RESULT) { + return CMD_FAILURE; + } + parameters[1] = (char*)temp.c_str(); + + if (IS_LOCAL(dest)) + { + // direct write, same server + user->WriteTo(dest, "PRIVMSG %s :%s", dest->nick, parameters[1]); + } + + FOREACH_MOD(I_OnUserMessage,OnUserMessage(user,dest,TYPE_USER,parameters[1],0,except_list)); + } + else + { + /* no such nick/channel */ + user->WriteServ("401 %s %s :No such nick/channel",user->nick, parameters[0]); + return CMD_FAILURE; + } + return CMD_SUCCESS; +} + diff --git a/src/cmd_qline.cpp b/src/cmd_qline.cpp index 892f6480c..bb1122854 100644 --- a/src/cmd_qline.cpp +++ b/src/cmd_qline.cpp @@ -1 +1,80 @@ -/* +------------------------------------+
* | 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 "users.h"
#include "modules.h"
#include "xline.h"
#include "commands/cmd_qline.h"
extern "C" DllExport command_t* init_command(InspIRCd* Instance)
{
return new cmd_qline(Instance);
}
CmdResult cmd_qline::Handle (const char** parameters, int pcnt, userrec *user)
{
if (pcnt >= 3)
{
if (ServerInstance->NickMatchesEveryone(parameters[0],user))
return CMD_FAILURE;
if (strchr(parameters[0],'@') || strchr(parameters[0],'!') || strchr(parameters[0],'.'))
{
user->WriteServ("NOTICE %s :*** A Q-Line only bans a nick pattern, not a nick!user@host pattern.",user->nick);
return CMD_FAILURE;
}
long duration = ServerInstance->Duration(parameters[1]);
if (ServerInstance->XLines->add_qline(duration,user->nick,parameters[2],parameters[0]))
{
int to_apply = APPLY_QLINES;
FOREACH_MOD(I_OnAddQLine,OnAddQLine(duration, user, parameters[2], parameters[0]));
if (!duration)
{
to_apply |= APPLY_PERM_ONLY;
ServerInstance->SNO->WriteToSnoMask('x',"%s added permanent Q-line for %s.",user->nick,parameters[0]);
}
else
{
time_t c_requires_crap = duration + ServerInstance->Time();
ServerInstance->SNO->WriteToSnoMask('x',"%s added timed Q-line for %s, expires on %s",user->nick,parameters[0],
ServerInstance->TimeString(c_requires_crap).c_str());
}
ServerInstance->XLines->apply_lines(to_apply);
}
else
{
user->WriteServ("NOTICE %s :*** Q-Line for %s already exists",user->nick,parameters[0]);
}
}
else
{
if (ServerInstance->XLines->del_qline(parameters[0]))
{
FOREACH_MOD(I_OnDelQLine,OnDelQLine(user, parameters[0]));
ServerInstance->SNO->WriteToSnoMask('x',"%s Removed Q-line on %s.",user->nick,parameters[0]);
}
else
{
user->WriteServ("NOTICE %s :*** Q-Line %s not found in list, try /stats q.",user->nick,parameters[0]);
return CMD_FAILURE;
}
}
return CMD_SUCCESS;
}
\ No newline at end of file +/* +------------------------------------+ + * | 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 "users.h" +#include "modules.h" +#include "xline.h" +#include "commands/cmd_qline.h" + + + +extern "C" DllExport command_t* init_command(InspIRCd* Instance) +{ + return new cmd_qline(Instance); +} + +CmdResult cmd_qline::Handle (const char** parameters, int pcnt, userrec *user) +{ + if (pcnt >= 3) + { + if (ServerInstance->NickMatchesEveryone(parameters[0],user)) + return CMD_FAILURE; + + if (strchr(parameters[0],'@') || strchr(parameters[0],'!') || strchr(parameters[0],'.')) + { + user->WriteServ("NOTICE %s :*** A Q-Line only bans a nick pattern, not a nick!user@host pattern.",user->nick); + return CMD_FAILURE; + } + + long duration = ServerInstance->Duration(parameters[1]); + if (ServerInstance->XLines->add_qline(duration,user->nick,parameters[2],parameters[0])) + { + int to_apply = APPLY_QLINES; + FOREACH_MOD(I_OnAddQLine,OnAddQLine(duration, user, parameters[2], parameters[0])); + if (!duration) + { + to_apply |= APPLY_PERM_ONLY; + ServerInstance->SNO->WriteToSnoMask('x',"%s added permanent Q-line for %s.",user->nick,parameters[0]); + } + else + { + time_t c_requires_crap = duration + ServerInstance->Time(); + ServerInstance->SNO->WriteToSnoMask('x',"%s added timed Q-line for %s, expires on %s",user->nick,parameters[0], + ServerInstance->TimeString(c_requires_crap).c_str()); + } + ServerInstance->XLines->apply_lines(to_apply); + } + else + { + user->WriteServ("NOTICE %s :*** Q-Line for %s already exists",user->nick,parameters[0]); + } + } + else + { + if (ServerInstance->XLines->del_qline(parameters[0])) + { + FOREACH_MOD(I_OnDelQLine,OnDelQLine(user, parameters[0])); + ServerInstance->SNO->WriteToSnoMask('x',"%s Removed Q-line on %s.",user->nick,parameters[0]); + } + else + { + user->WriteServ("NOTICE %s :*** Q-Line %s not found in list, try /stats q.",user->nick,parameters[0]); + return CMD_FAILURE; + } + } + + return CMD_SUCCESS; +} + diff --git a/src/cmd_quit.cpp b/src/cmd_quit.cpp index 45e970207..a859c1790 100644 --- a/src/cmd_quit.cpp +++ b/src/cmd_quit.cpp @@ -1 +1,48 @@ -/* +------------------------------------+
* | 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 "users.h"
#include "modules.h"
#include "commands/cmd_quit.h"
extern "C" DllExport command_t* init_command(InspIRCd* Instance)
{
return new cmd_quit(Instance);
}
CmdResult cmd_quit::Handle (const char** parameters, int pcnt, userrec *user)
{
std::string quitmsg;
if (IS_LOCAL(user))
{
if (*ServerInstance->Config->FixedQuit)
quitmsg = ServerInstance->Config->FixedQuit;
else
quitmsg = pcnt ?
ServerInstance->Config->PrefixQuit + std::string(parameters[0]) + ServerInstance->Config->SuffixQuit
: "Client exited";
}
else
quitmsg = pcnt ? parameters[0] : "Client exited";
userrec::QuitUser(ServerInstance, user, quitmsg);
return CMD_SUCCESS;
}
\ No newline at end of file +/* +------------------------------------+ + * | 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 "users.h" +#include "modules.h" +#include "commands/cmd_quit.h" + + + +extern "C" DllExport command_t* init_command(InspIRCd* Instance) +{ + return new cmd_quit(Instance); +} + +CmdResult cmd_quit::Handle (const char** parameters, int pcnt, userrec *user) +{ + + std::string quitmsg; + + if (IS_LOCAL(user)) + { + if (*ServerInstance->Config->FixedQuit) + quitmsg = ServerInstance->Config->FixedQuit; + else + quitmsg = pcnt ? + ServerInstance->Config->PrefixQuit + std::string(parameters[0]) + ServerInstance->Config->SuffixQuit + : "Client exited"; + } + else + quitmsg = pcnt ? parameters[0] : "Client exited"; + + userrec::QuitUser(ServerInstance, user, quitmsg); + + return CMD_SUCCESS; +} + diff --git a/src/cmd_rehash.cpp b/src/cmd_rehash.cpp index c8415f313..34789b0ea 100644 --- a/src/cmd_rehash.cpp +++ b/src/cmd_rehash.cpp @@ -1 +1,56 @@ -/* +------------------------------------+
* | 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 "users.h"
#include "modules.h"
#include "commands/cmd_rehash.h"
extern "C" DllExport command_t* init_command(InspIRCd* Instance)
{
return new cmd_rehash(Instance);
}
CmdResult cmd_rehash::Handle (const char** parameters, int pcnt, userrec *user)
{
user->WriteServ("382 %s %s :Rehashing",user->nick,ServerConfig::CleanFilename(ServerInstance->ConfigFileName));
std::string parameter;
std::string old_disabled = ServerInstance->Config->DisabledCommands;
if (pcnt)
{
parameter = parameters[0];
}
else
{
ServerInstance->WriteOpers("*** %s is rehashing config file %s",user->nick,ServerConfig::CleanFilename(ServerInstance->ConfigFileName));
ServerInstance->CloseLog();
ServerInstance->OpenLog(ServerInstance->Config->argv, ServerInstance->Config->argc);
ServerInstance->RehashUsersAndChans();
FOREACH_MOD(I_OnGarbageCollect, OnGarbageCollect());
ServerInstance->Config->Read(false,user);
ServerInstance->Res->Rehash();
ServerInstance->ResetMaxBans();
}
if (old_disabled != ServerInstance->Config->DisabledCommands)
InitializeDisabledCommands(ServerInstance->Config->DisabledCommands, ServerInstance);
FOREACH_MOD(I_OnRehash,OnRehash(user, parameter));
ServerInstance->BuildISupport();
return CMD_SUCCESS;
}
\ No newline at end of file +/* +------------------------------------+ + * | 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 "users.h" +#include "modules.h" +#include "commands/cmd_rehash.h" + + + +extern "C" DllExport command_t* init_command(InspIRCd* Instance) +{ + return new cmd_rehash(Instance); +} + +CmdResult cmd_rehash::Handle (const char** parameters, int pcnt, userrec *user) +{ + user->WriteServ("382 %s %s :Rehashing",user->nick,ServerConfig::CleanFilename(ServerInstance->ConfigFileName)); + std::string parameter; + std::string old_disabled = ServerInstance->Config->DisabledCommands; + if (pcnt) + { + parameter = parameters[0]; + } + else + { + ServerInstance->WriteOpers("*** %s is rehashing config file %s",user->nick,ServerConfig::CleanFilename(ServerInstance->ConfigFileName)); + ServerInstance->CloseLog(); + ServerInstance->OpenLog(ServerInstance->Config->argv, ServerInstance->Config->argc); + ServerInstance->RehashUsersAndChans(); + FOREACH_MOD(I_OnGarbageCollect, OnGarbageCollect()); + ServerInstance->Config->Read(false,user); + ServerInstance->Res->Rehash(); + ServerInstance->ResetMaxBans(); + } + if (old_disabled != ServerInstance->Config->DisabledCommands) + InitializeDisabledCommands(ServerInstance->Config->DisabledCommands, ServerInstance); + + FOREACH_MOD(I_OnRehash,OnRehash(user, parameter)); + + ServerInstance->BuildISupport(); + + return CMD_SUCCESS; +} + diff --git a/src/cmd_reloadmodule.cpp b/src/cmd_reloadmodule.cpp index 0eb6f1ec4..41da0c40d 100644 --- a/src/cmd_reloadmodule.cpp +++ b/src/cmd_reloadmodule.cpp @@ -1 +1,39 @@ -/* +------------------------------------+
* | 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 "users.h"
#include "commands/cmd_reloadmodule.h"
extern "C" DllExport command_t* init_command(InspIRCd* Instance)
{
return new cmd_reloadmodule(Instance);
}
CmdResult cmd_reloadmodule::Handle (const char** parameters, int pcnt, userrec *user)
{
if (ServerInstance->UnloadModule(parameters[0]))
{
ServerInstance->WriteOpers("*** RELOAD MODULE: %s unloaded %s",user->nick, parameters[0]);
if (ServerInstance->LoadModule(parameters[0]))
{
ServerInstance->WriteOpers("*** RELOAD MODULE: %s reloaded %s",user->nick, parameters[0]);
user->WriteServ("975 %s %s :Module successfully reloaded.",user->nick, parameters[0]);
return CMD_SUCCESS;
}
}
ServerInstance->WriteOpers("*** RELOAD MODULE: %s unsuccessfully reloaded %s",user->nick, parameters[0]);
user->WriteServ("975 %s %s :Module failed to reload.",user->nick, parameters[0]);
return CMD_FAILURE;
}
\ No newline at end of file +/* +------------------------------------+ + * | 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 "users.h" +#include "commands/cmd_reloadmodule.h" + +extern "C" DllExport command_t* init_command(InspIRCd* Instance) +{ + return new cmd_reloadmodule(Instance); +} + +CmdResult cmd_reloadmodule::Handle (const char** parameters, int pcnt, userrec *user) +{ + if (ServerInstance->UnloadModule(parameters[0])) + { + ServerInstance->WriteOpers("*** RELOAD MODULE: %s unloaded %s",user->nick, parameters[0]); + if (ServerInstance->LoadModule(parameters[0])) + { + ServerInstance->WriteOpers("*** RELOAD MODULE: %s reloaded %s",user->nick, parameters[0]); + user->WriteServ("975 %s %s :Module successfully reloaded.",user->nick, parameters[0]); + return CMD_SUCCESS; + } + } + + ServerInstance->WriteOpers("*** RELOAD MODULE: %s unsuccessfully reloaded %s",user->nick, parameters[0]); + user->WriteServ("975 %s %s :Module failed to reload.",user->nick, parameters[0]); + return CMD_FAILURE; +} diff --git a/src/cmd_restart.cpp b/src/cmd_restart.cpp index 1dbf5b121..c910df8ba 100644 --- a/src/cmd_restart.cpp +++ b/src/cmd_restart.cpp @@ -1 +1,49 @@ -/* +------------------------------------+
* | 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 "users.h"
#include "commands/cmd_restart.h"
extern "C" DllExport command_t* init_command(InspIRCd* Instance)
{
return new cmd_restart(Instance);
}
CmdResult cmd_restart::Handle (const char** parameters, int pcnt, userrec *user)
{
ServerInstance->Log(DEFAULT,"Restart: %s",user->nick);
if (!strcmp(parameters[0],ServerInstance->Config->restartpass))
{
ServerInstance->WriteOpers("*** RESTART command from %s!%s@%s, restarting server.",user->nick,user->ident,user->host);
try
{
ServerInstance->Restart("Server restarting.");
}
catch (...)
{
/* We dont actually get here unless theres some fatal and unrecoverable error. */
exit(0);
}
}
else
{
ServerInstance->WriteOpers("*** Failed RESTART Command from %s!%s@%s.",user->nick,user->ident,user->host);
return CMD_FAILURE;
}
return CMD_SUCCESS;
}
\ No newline at end of file +/* +------------------------------------+ + * | 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 "users.h" +#include "commands/cmd_restart.h" + +extern "C" DllExport command_t* init_command(InspIRCd* Instance) +{ + return new cmd_restart(Instance); +} + +CmdResult cmd_restart::Handle (const char** parameters, int pcnt, userrec *user) +{ + ServerInstance->Log(DEFAULT,"Restart: %s",user->nick); + if (!strcmp(parameters[0],ServerInstance->Config->restartpass)) + { + ServerInstance->WriteOpers("*** RESTART command from %s!%s@%s, restarting server.",user->nick,user->ident,user->host); + + try + { + ServerInstance->Restart("Server restarting."); + } + catch (...) + { + /* We dont actually get here unless theres some fatal and unrecoverable error. */ + exit(0); + } + } + else + { + ServerInstance->WriteOpers("*** Failed RESTART Command from %s!%s@%s.",user->nick,user->ident,user->host); + return CMD_FAILURE; + } + + return CMD_SUCCESS; +} + diff --git a/src/cmd_rules.cpp b/src/cmd_rules.cpp index ef531732d..95b744412 100644 --- a/src/cmd_rules.cpp +++ b/src/cmd_rules.cpp @@ -1 +1,27 @@ -/* +------------------------------------+
* | 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 "users.h"
#include "commands/cmd_rules.h"
extern "C" DllExport command_t* init_command(InspIRCd* Instance)
{
return new cmd_rules(Instance);
}
CmdResult cmd_rules::Handle (const char** parameters, int pcnt, userrec *user)
{
user->ShowRULES();
return CMD_SUCCESS;
}
\ No newline at end of file +/* +------------------------------------+ + * | 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 "users.h" +#include "commands/cmd_rules.h" + +extern "C" DllExport command_t* init_command(InspIRCd* Instance) +{ + return new cmd_rules(Instance); +} + +CmdResult cmd_rules::Handle (const char** parameters, int pcnt, userrec *user) +{ + user->ShowRULES(); + return CMD_SUCCESS; +} diff --git a/src/cmd_server.cpp b/src/cmd_server.cpp index acad55b1b..cace13c38 100644 --- a/src/cmd_server.cpp +++ b/src/cmd_server.cpp @@ -1 +1,30 @@ -/* +------------------------------------+
* | 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 "users.h"
#include "commands/cmd_server.h"
extern "C" DllExport command_t* init_command(InspIRCd* Instance)
{
return new cmd_server(Instance);
}
CmdResult cmd_server::Handle (const char** parameters, int pcnt, userrec *user)
{
user->WriteServ("666 %s :You cannot identify as a server, you are a USER. IRC Operators informed.",user->nick);
ServerInstance->WriteOpers("*** WARNING: %s attempted to issue a SERVER command and is registered as a user!",user->nick);
return CMD_FAILURE;
}
\ No newline at end of file +/* +------------------------------------+ + * | 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 "users.h" +#include "commands/cmd_server.h" + + + +extern "C" DllExport command_t* init_command(InspIRCd* Instance) +{ + return new cmd_server(Instance); +} + +CmdResult cmd_server::Handle (const char** parameters, int pcnt, userrec *user) +{ + user->WriteServ("666 %s :You cannot identify as a server, you are a USER. IRC Operators informed.",user->nick); + ServerInstance->WriteOpers("*** WARNING: %s attempted to issue a SERVER command and is registered as a user!",user->nick); + return CMD_FAILURE; +} diff --git a/src/cmd_squit.cpp b/src/cmd_squit.cpp index d6fe63c94..57105109b 100644 --- a/src/cmd_squit.cpp +++ b/src/cmd_squit.cpp @@ -1 +1,32 @@ -/* +------------------------------------+
* | 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 "users.h"
#include "commands/cmd_squit.h"
/*
* This is handled by the server linking module, if necessary. Do not remove this stub.
*/
extern "C" DllExport command_t* init_command(InspIRCd* Instance)
{
return new cmd_squit(Instance);
}
CmdResult cmd_squit::Handle (const char** parameters, int pcnt, userrec *user)
{
user->WriteServ( "NOTICE %s :You are a nub. Load a linking module.", user->nick);
return CMD_FAILURE;
}
\ No newline at end of file +/* +------------------------------------+ + * | 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 "users.h" +#include "commands/cmd_squit.h" + +/* + * This is handled by the server linking module, if necessary. Do not remove this stub. + */ + + +extern "C" DllExport command_t* init_command(InspIRCd* Instance) +{ + return new cmd_squit(Instance); +} + +CmdResult cmd_squit::Handle (const char** parameters, int pcnt, userrec *user) +{ + user->WriteServ( "NOTICE %s :You are a nub. Load a linking module.", user->nick); + return CMD_FAILURE; +} diff --git a/src/cmd_stats.cpp b/src/cmd_stats.cpp index 4bf872981..98b2c63dd 100644 --- a/src/cmd_stats.cpp +++ b/src/cmd_stats.cpp @@ -1 +1,318 @@ -/* +------------------------------------+
* | 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"
#ifndef WIN32
#include <sys/resource.h>
/* This is just to be completely certain that the change which fixed getrusage on RH7 doesn't break anything else -- Om */
#ifndef RUSAGE_SELF
#define RUSAGE_SELF 0
#endif
#endif
#include "users.h"
#include "modules.h"
#include "xline.h"
#include "commands/cmd_stats.h"
#include "commands/cmd_whowas.h"
extern "C" DllExport command_t* init_command(InspIRCd* Instance)
{
return new cmd_stats(Instance);
}
CmdResult cmd_stats::Handle (const char** parameters, int pcnt, userrec *user)
{
if (IS_LOCAL(user))
{
string_list values;
DoStats(this->ServerInstance, *parameters[0], user, values);
for (size_t i = 0; i < values.size(); i++)
user->Write(":%s", values[i].c_str());
}
return CMD_SUCCESS;
}
DllExport void DoStats(InspIRCd* ServerInstance, char statschar, userrec* user, string_list &results)
{
std::string sn = ServerInstance->Config->ServerName;
if ((*ServerInstance->Config->UserStats) && !IS_OPER(user) && !strchr(ServerInstance->Config->UserStats,statschar))
{
results.push_back(sn+std::string(" 481 ")+user->nick+" :Permission denied - STATS "+statschar+" is oper-only");
return;
}
int MOD_RESULT = 0;
FOREACH_RESULT(I_OnStats,OnStats(statschar,user,results));
if (MOD_RESULT)
return;
switch (statschar)
{
/* stats p (show listening ports and registered clients on each) */
case 'p':
{
for (size_t i = 0; i < ServerInstance->Config->ports.size(); i++)
{
std::string ip = ServerInstance->Config->ports[i]->GetIP();
if (ip.empty())
ip.assign("*");
results.push_back(sn+" 249 "+user->nick+" :"+ ip + ":"+ConvToStr(ServerInstance->Config->ports[i]->GetPort())+" (client, " +
ServerInstance->Config->ports[i]->GetDescription() + ")");
}
}
break;
case 'n':
case 'c':
{
/* This stats symbol must be handled by a linking module */
}
break;
case 'i':
{
int idx = 0;
for (ClassVector::iterator i = ServerInstance->Config->Classes.begin(); i != ServerInstance->Config->Classes.end(); i++)
{
results.push_back(sn+" 215 "+user->nick+" I NOMATCH * "+i->GetHost()+" "+ConvToStr(MAXCLIENTS)+" "+ConvToStr(idx)+" "+ServerInstance->Config->ServerName+" *");
idx++;
}
}
break;
case 'Y':
{
int idx = 0;
for (ClassVector::iterator i = ServerInstance->Config->Classes.begin(); i != ServerInstance->Config->Classes.end(); i++)
{
results.push_back(sn+" 218 "+user->nick+" Y "+ConvToStr(idx)+" "+ConvToStr(i->GetPingTime())+" 0 "+ConvToStr(i->GetSendqMax())+" :"+
ConvToStr(i->GetFlood())+" "+ConvToStr(i->GetRegTimeout()));
idx++;
}
}
break;
case 'U':
{
char ulined[MAXBUF];
for (int i = 0; i < ServerInstance->Config->ConfValueEnum(ServerInstance->Config->config_data, "uline"); i++)
{
ServerInstance->Config->ConfValue(ServerInstance->Config->config_data, "uline","server", i, ulined, MAXBUF);
results.push_back(sn+" 248 "+user->nick+" U "+std::string(ulined));
}
}
break;
case 'P':
{
int idx = 0;
for (user_hash::iterator i = ServerInstance->clientlist->begin(); i != ServerInstance->clientlist->end(); i++)
{
if (IS_OPER(i->second) && !ServerInstance->ULine(i->second->server))
{
results.push_back(sn+" 249 "+user->nick+" :"+i->second->nick+" ("+i->second->ident+"@"+i->second->dhost+") Idle: "+
(IS_LOCAL(i->second) ? ConvToStr(ServerInstance->Time() - i->second->idle_lastmsg) + " secs" : "unavailable"));
idx++;
}
}
results.push_back(sn+" 249 "+user->nick+" :"+ConvToStr(idx)+" OPER(s)");
}
break;
case 'k':
ServerInstance->XLines->stats_k(user,results);
break;
case 'g':
ServerInstance->XLines->stats_g(user,results);
break;
case 'q':
ServerInstance->XLines->stats_q(user,results);
break;
case 'Z':
ServerInstance->XLines->stats_z(user,results);
break;
case 'e':
ServerInstance->XLines->stats_e(user,results);
break;
/* stats m (list number of times each command has been used, plus bytecount) */
case 'm':
for (command_table::iterator i = ServerInstance->Parser->cmdlist.begin(); i != ServerInstance->Parser->cmdlist.end(); i++)
{
if (i->second->use_count)
{
/* RPL_STATSCOMMANDS */
results.push_back(sn+" 212 "+user->nick+" "+i->second->command+" "+ConvToStr(i->second->use_count)+" "+ConvToStr(i->second->total_bytes));
}
}
break;
/* stats z (debug and memory info) */
case 'z':
{
results.push_back(sn+" 240 "+user->nick+" :InspIRCd(CLASS) "+ConvToStr(sizeof(InspIRCd))+" bytes");
results.push_back(sn+" 249 "+user->nick+" :Users(HASH_MAP) "+ConvToStr(ServerInstance->clientlist->size())+" ("+ConvToStr(ServerInstance->clientlist->size()*sizeof(userrec))+" bytes)");
results.push_back(sn+" 249 "+user->nick+" :Channels(HASH_MAP) "+ConvToStr(ServerInstance->chanlist->size())+" ("+ConvToStr(ServerInstance->chanlist->size()*sizeof(chanrec))+" bytes)");
results.push_back(sn+" 249 "+user->nick+" :Commands(VECTOR) "+ConvToStr(ServerInstance->Parser->cmdlist.size())+" ("+ConvToStr(ServerInstance->Parser->cmdlist.size()*sizeof(command_t))+" bytes)");
if (!ServerInstance->Config->WhoWasGroupSize == 0 && !ServerInstance->Config->WhoWasMaxGroups == 0)
{
command_t* whowas_command = ServerInstance->Parser->GetHandler("WHOWAS");
if (whowas_command)
{
std::deque<classbase*> params;
Extensible whowas_stats;
params.push_back(&whowas_stats);
whowas_command->HandleInternal(WHOWAS_STATS, params);
if (whowas_stats.GetExt("stats"))
{
char* stats;
whowas_stats.GetExt("stats", stats);
results.push_back(sn+" 249 "+user->nick+" :"+ConvToStr(stats));
}
}
}
results.push_back(sn+" 249 "+user->nick+" :MOTD(VECTOR) "+ConvToStr(ServerInstance->Config->MOTD.size())+", RULES(VECTOR) "+ConvToStr(ServerInstance->Config->RULES.size()));
results.push_back(sn+" 249 "+user->nick+" :Modules(VECTOR) "+ConvToStr(ServerInstance->modules.size())+" ("+ConvToStr(ServerInstance->modules.size()*sizeof(Module))+" bytes)");
results.push_back(sn+" 249 "+user->nick+" :ClassFactories(VECTOR) "+ConvToStr(ServerInstance->factory.size())+" ("+ConvToStr(ServerInstance->factory.size()*sizeof(ircd_module))+" bytes)");
#ifndef WIN32
/* Moved this down here so all the not-windows stuff (look w00tie, I didn't say win32!) is in one ifndef.
* Also cuts out some identical code in both branches of the ifndef. -- Om
*/
rusage R;
/* Not sure why we were doing '0' with a RUSAGE_SELF comment rather than just using RUSAGE_SELF -- Om */
if (!getrusage(RUSAGE_SELF,&R)) /* RUSAGE_SELF */
{
results.push_back(sn+" 249 "+user->nick+" :Total allocation: "+ConvToStr(R.ru_maxrss)+"K");
results.push_back(sn+" 249 "+user->nick+" :Signals: "+ConvToStr(R.ru_nsignals));
results.push_back(sn+" 249 "+user->nick+" :Page faults: "+ConvToStr(R.ru_majflt));
results.push_back(sn+" 249 "+user->nick+" :Swaps: "+ConvToStr(R.ru_nswap));
results.push_back(sn+" 249 "+user->nick+" :Context Switches: Voluntary; "+ConvToStr(R.ru_nvcsw)+" Involuntary; "+ConvToStr(R.ru_nivcsw));
timeval tv;
char percent[30];
gettimeofday(&tv, NULL);
float n_elapsed = ((tv.tv_sec - ServerInstance->stats->LastSampled.tv_sec) * 1000000 + tv.tv_usec - ServerInstance->stats->LastSampled.tv_usec);
float n_eaten = ((R.ru_utime.tv_sec - ServerInstance->stats->LastCPU.tv_sec) * 1000000 + R.ru_utime.tv_usec - ServerInstance->stats->LastCPU.tv_usec);
float per = (n_eaten / n_elapsed) * 100;
snprintf(percent, 30, "%03.5f%%", per);
results.push_back(sn+" 249 "+user->nick+" :CPU Usage: "+percent);
}
#endif
}
break;
case 'T':
{
char buffer[MAXBUF];
results.push_back(sn+" 249 "+user->nick+" :accepts "+ConvToStr(ServerInstance->stats->statsAccept)+" refused "+ConvToStr(ServerInstance->stats->statsRefused));
results.push_back(sn+" 249 "+user->nick+" :unknown commands "+ConvToStr(ServerInstance->stats->statsUnknown));
results.push_back(sn+" 249 "+user->nick+" :nick collisions "+ConvToStr(ServerInstance->stats->statsCollisions));
results.push_back(sn+" 249 "+user->nick+" :dns requests "+ConvToStr(ServerInstance->stats->statsDnsGood+ServerInstance->stats->statsDnsBad)+" succeeded "+ConvToStr(ServerInstance->stats->statsDnsGood)+" failed "+ConvToStr(ServerInstance->stats->statsDnsBad));
results.push_back(sn+" 249 "+user->nick+" :connection count "+ConvToStr(ServerInstance->stats->statsConnects));
snprintf(buffer,MAXBUF," 249 %s :bytes sent %5.2fK recv %5.2fK",user->nick,ServerInstance->stats->statsSent / 1024,ServerInstance->stats->statsRecv / 1024);
results.push_back(sn+buffer);
}
break;
/* stats o */
case 'o':
for (int i = 0; i < ServerInstance->Config->ConfValueEnum(ServerInstance->Config->config_data, "oper"); i++)
{
char LoginName[MAXBUF];
char HostName[MAXBUF];
char OperType[MAXBUF];
ServerInstance->Config->ConfValue(ServerInstance->Config->config_data, "oper","name", i, LoginName, MAXBUF);
ServerInstance->Config->ConfValue(ServerInstance->Config->config_data, "oper","host", i, HostName, MAXBUF);
ServerInstance->Config->ConfValue(ServerInstance->Config->config_data, "oper","type", i, OperType, MAXBUF);
results.push_back(sn+" 243 "+user->nick+" O "+HostName+" * "+LoginName+" "+OperType+" 0");
}
break;
/* stats l (show user I/O stats) */
case 'l':
results.push_back(sn+" 211 "+user->nick+" :nick[ident@host] sendq cmds_out bytes_out cmds_in bytes_in time_open");
for (std::vector<userrec*>::iterator n = ServerInstance->local_users.begin(); n != ServerInstance->local_users.end(); n++)
{
userrec* i = *n;
if (ServerInstance->IsNick(i->nick))
{
results.push_back(sn+" 211 "+user->nick+" "+i->nick+"["+i->ident+"@"+i->dhost+"] "+ConvToStr(i->sendq.length())+" "+ConvToStr(i->cmds_out)+" "+ConvToStr(i->bytes_out)+" "+ConvToStr(i->cmds_in)+" "+ConvToStr(i->bytes_in)+" "+ConvToStr(ServerInstance->Time() - i->age));
}
}
break;
/* stats L (show user I/O stats with IP addresses) */
case 'L':
results.push_back(sn+" 211 "+user->nick+" :nick[ident@ip] sendq cmds_out bytes_out cmds_in bytes_in time_open");
for (std::vector<userrec*>::iterator n = ServerInstance->local_users.begin(); n != ServerInstance->local_users.end(); n++)
{
userrec* i = *n;
if (ServerInstance->IsNick(i->nick))
{
results.push_back(sn+" 211 "+user->nick+" "+i->nick+"["+i->ident+"@"+i->GetIPString()+"] "+ConvToStr(i->sendq.length())+" "+ConvToStr(i->cmds_out)+" "+ConvToStr(i->bytes_out)+" "+ConvToStr(i->cmds_in)+" "+ConvToStr(i->bytes_in)+" "+ConvToStr(ServerInstance->Time() - i->age));
}
}
break;
/* stats u (show server uptime) */
case 'u':
{
time_t current_time = 0;
current_time = ServerInstance->Time();
time_t server_uptime = current_time - ServerInstance->startup_time;
struct tm* stime;
stime = gmtime(&server_uptime);
/* i dont know who the hell would have an ircd running for over a year nonstop, but
* Craig suggested this, and it seemed a good idea so in it went */
if (stime->tm_year > 70)
{
char buffer[MAXBUF];
snprintf(buffer,MAXBUF," 242 %s :Server up %d years, %d days, %.2d:%.2d:%.2d",user->nick,(stime->tm_year-70),stime->tm_yday,stime->tm_hour,stime->tm_min,stime->tm_sec);
results.push_back(sn+buffer);
}
else
{
char buffer[MAXBUF];
snprintf(buffer,MAXBUF," 242 %s :Server up %d days, %.2d:%.2d:%.2d",user->nick,stime->tm_yday,stime->tm_hour,stime->tm_min,stime->tm_sec);
results.push_back(sn+buffer);
}
}
break;
default:
break;
}
results.push_back(sn+" 219 "+user->nick+" "+statschar+" :End of /STATS report");
ServerInstance->SNO->WriteToSnoMask('t',"%s '%c' requested by %s (%s@%s)",(!strcmp(user->server,ServerInstance->Config->ServerName) ? "Stats" : "Remote stats"),statschar,user->nick,user->ident,user->host);
return;
}
\ No newline at end of file +/* +------------------------------------+ + * | 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" +#ifndef WIN32 +#include <sys/resource.h> + +/* This is just to be completely certain that the change which fixed getrusage on RH7 doesn't break anything else -- Om */ +#ifndef RUSAGE_SELF +#define RUSAGE_SELF 0 +#endif + +#endif +#include "users.h" +#include "modules.h" +#include "xline.h" +#include "commands/cmd_stats.h" +#include "commands/cmd_whowas.h" + + +extern "C" DllExport command_t* init_command(InspIRCd* Instance) +{ + return new cmd_stats(Instance); +} + +CmdResult cmd_stats::Handle (const char** parameters, int pcnt, userrec *user) +{ + if (IS_LOCAL(user)) + { + string_list values; + DoStats(this->ServerInstance, *parameters[0], user, values); + for (size_t i = 0; i < values.size(); i++) + user->Write(":%s", values[i].c_str()); + } + + return CMD_SUCCESS; +} + +DllExport void DoStats(InspIRCd* ServerInstance, char statschar, userrec* user, string_list &results) +{ + std::string sn = ServerInstance->Config->ServerName; + + if ((*ServerInstance->Config->UserStats) && !IS_OPER(user) && !strchr(ServerInstance->Config->UserStats,statschar)) + { + results.push_back(sn+std::string(" 481 ")+user->nick+" :Permission denied - STATS "+statschar+" is oper-only"); + return; + } + + int MOD_RESULT = 0; + FOREACH_RESULT(I_OnStats,OnStats(statschar,user,results)); + if (MOD_RESULT) + return; + + switch (statschar) + { + /* stats p (show listening ports and registered clients on each) */ + case 'p': + { + for (size_t i = 0; i < ServerInstance->Config->ports.size(); i++) + { + std::string ip = ServerInstance->Config->ports[i]->GetIP(); + if (ip.empty()) + ip.assign("*"); + + results.push_back(sn+" 249 "+user->nick+" :"+ ip + ":"+ConvToStr(ServerInstance->Config->ports[i]->GetPort())+" (client, " + + ServerInstance->Config->ports[i]->GetDescription() + ")"); + } + } + break; + + case 'n': + case 'c': + { + /* This stats symbol must be handled by a linking module */ + } + break; + + case 'i': + { + int idx = 0; + for (ClassVector::iterator i = ServerInstance->Config->Classes.begin(); i != ServerInstance->Config->Classes.end(); i++) + { + results.push_back(sn+" 215 "+user->nick+" I NOMATCH * "+i->GetHost()+" "+ConvToStr(MAXCLIENTS)+" "+ConvToStr(idx)+" "+ServerInstance->Config->ServerName+" *"); + idx++; + } + } + break; + + case 'Y': + { + int idx = 0; + for (ClassVector::iterator i = ServerInstance->Config->Classes.begin(); i != ServerInstance->Config->Classes.end(); i++) + { + results.push_back(sn+" 218 "+user->nick+" Y "+ConvToStr(idx)+" "+ConvToStr(i->GetPingTime())+" 0 "+ConvToStr(i->GetSendqMax())+" :"+ + ConvToStr(i->GetFlood())+" "+ConvToStr(i->GetRegTimeout())); + idx++; + } + } + break; + + case 'U': + { + char ulined[MAXBUF]; + for (int i = 0; i < ServerInstance->Config->ConfValueEnum(ServerInstance->Config->config_data, "uline"); i++) + { + ServerInstance->Config->ConfValue(ServerInstance->Config->config_data, "uline","server", i, ulined, MAXBUF); + results.push_back(sn+" 248 "+user->nick+" U "+std::string(ulined)); + } + } + break; + + case 'P': + { + int idx = 0; + for (user_hash::iterator i = ServerInstance->clientlist->begin(); i != ServerInstance->clientlist->end(); i++) + { + if (IS_OPER(i->second) && !ServerInstance->ULine(i->second->server)) + { + results.push_back(sn+" 249 "+user->nick+" :"+i->second->nick+" ("+i->second->ident+"@"+i->second->dhost+") Idle: "+ + (IS_LOCAL(i->second) ? ConvToStr(ServerInstance->Time() - i->second->idle_lastmsg) + " secs" : "unavailable")); + idx++; + } + } + results.push_back(sn+" 249 "+user->nick+" :"+ConvToStr(idx)+" OPER(s)"); + } + break; + + case 'k': + ServerInstance->XLines->stats_k(user,results); + break; + + case 'g': + ServerInstance->XLines->stats_g(user,results); + break; + + case 'q': + ServerInstance->XLines->stats_q(user,results); + break; + + case 'Z': + ServerInstance->XLines->stats_z(user,results); + break; + + case 'e': + ServerInstance->XLines->stats_e(user,results); + break; + + /* stats m (list number of times each command has been used, plus bytecount) */ + case 'm': + for (command_table::iterator i = ServerInstance->Parser->cmdlist.begin(); i != ServerInstance->Parser->cmdlist.end(); i++) + { + if (i->second->use_count) + { + /* RPL_STATSCOMMANDS */ + results.push_back(sn+" 212 "+user->nick+" "+i->second->command+" "+ConvToStr(i->second->use_count)+" "+ConvToStr(i->second->total_bytes)); + } + } + break; + + /* stats z (debug and memory info) */ + case 'z': + { + results.push_back(sn+" 240 "+user->nick+" :InspIRCd(CLASS) "+ConvToStr(sizeof(InspIRCd))+" bytes"); + results.push_back(sn+" 249 "+user->nick+" :Users(HASH_MAP) "+ConvToStr(ServerInstance->clientlist->size())+" ("+ConvToStr(ServerInstance->clientlist->size()*sizeof(userrec))+" bytes)"); + results.push_back(sn+" 249 "+user->nick+" :Channels(HASH_MAP) "+ConvToStr(ServerInstance->chanlist->size())+" ("+ConvToStr(ServerInstance->chanlist->size()*sizeof(chanrec))+" bytes)"); + results.push_back(sn+" 249 "+user->nick+" :Commands(VECTOR) "+ConvToStr(ServerInstance->Parser->cmdlist.size())+" ("+ConvToStr(ServerInstance->Parser->cmdlist.size()*sizeof(command_t))+" bytes)"); + + if (!ServerInstance->Config->WhoWasGroupSize == 0 && !ServerInstance->Config->WhoWasMaxGroups == 0) + { + command_t* whowas_command = ServerInstance->Parser->GetHandler("WHOWAS"); + if (whowas_command) + { + std::deque<classbase*> params; + Extensible whowas_stats; + params.push_back(&whowas_stats); + whowas_command->HandleInternal(WHOWAS_STATS, params); + if (whowas_stats.GetExt("stats")) + { + char* stats; + whowas_stats.GetExt("stats", stats); + results.push_back(sn+" 249 "+user->nick+" :"+ConvToStr(stats)); + } + } + } + + results.push_back(sn+" 249 "+user->nick+" :MOTD(VECTOR) "+ConvToStr(ServerInstance->Config->MOTD.size())+", RULES(VECTOR) "+ConvToStr(ServerInstance->Config->RULES.size())); + results.push_back(sn+" 249 "+user->nick+" :Modules(VECTOR) "+ConvToStr(ServerInstance->modules.size())+" ("+ConvToStr(ServerInstance->modules.size()*sizeof(Module))+" bytes)"); + results.push_back(sn+" 249 "+user->nick+" :ClassFactories(VECTOR) "+ConvToStr(ServerInstance->factory.size())+" ("+ConvToStr(ServerInstance->factory.size()*sizeof(ircd_module))+" bytes)"); + +#ifndef WIN32 + /* Moved this down here so all the not-windows stuff (look w00tie, I didn't say win32!) is in one ifndef. + * Also cuts out some identical code in both branches of the ifndef. -- Om + */ + rusage R; + + /* Not sure why we were doing '0' with a RUSAGE_SELF comment rather than just using RUSAGE_SELF -- Om */ + if (!getrusage(RUSAGE_SELF,&R)) /* RUSAGE_SELF */ + { + results.push_back(sn+" 249 "+user->nick+" :Total allocation: "+ConvToStr(R.ru_maxrss)+"K"); + results.push_back(sn+" 249 "+user->nick+" :Signals: "+ConvToStr(R.ru_nsignals)); + results.push_back(sn+" 249 "+user->nick+" :Page faults: "+ConvToStr(R.ru_majflt)); + results.push_back(sn+" 249 "+user->nick+" :Swaps: "+ConvToStr(R.ru_nswap)); + results.push_back(sn+" 249 "+user->nick+" :Context Switches: Voluntary; "+ConvToStr(R.ru_nvcsw)+" Involuntary; "+ConvToStr(R.ru_nivcsw)); + + timeval tv; + char percent[30]; + gettimeofday(&tv, NULL); + + float n_elapsed = ((tv.tv_sec - ServerInstance->stats->LastSampled.tv_sec) * 1000000 + tv.tv_usec - ServerInstance->stats->LastSampled.tv_usec); + float n_eaten = ((R.ru_utime.tv_sec - ServerInstance->stats->LastCPU.tv_sec) * 1000000 + R.ru_utime.tv_usec - ServerInstance->stats->LastCPU.tv_usec); + float per = (n_eaten / n_elapsed) * 100; + + snprintf(percent, 30, "%03.5f%%", per); + results.push_back(sn+" 249 "+user->nick+" :CPU Usage: "+percent); + } +#endif + } + break; + + case 'T': + { + char buffer[MAXBUF]; + results.push_back(sn+" 249 "+user->nick+" :accepts "+ConvToStr(ServerInstance->stats->statsAccept)+" refused "+ConvToStr(ServerInstance->stats->statsRefused)); + results.push_back(sn+" 249 "+user->nick+" :unknown commands "+ConvToStr(ServerInstance->stats->statsUnknown)); + results.push_back(sn+" 249 "+user->nick+" :nick collisions "+ConvToStr(ServerInstance->stats->statsCollisions)); + results.push_back(sn+" 249 "+user->nick+" :dns requests "+ConvToStr(ServerInstance->stats->statsDnsGood+ServerInstance->stats->statsDnsBad)+" succeeded "+ConvToStr(ServerInstance->stats->statsDnsGood)+" failed "+ConvToStr(ServerInstance->stats->statsDnsBad)); + results.push_back(sn+" 249 "+user->nick+" :connection count "+ConvToStr(ServerInstance->stats->statsConnects)); + snprintf(buffer,MAXBUF," 249 %s :bytes sent %5.2fK recv %5.2fK",user->nick,ServerInstance->stats->statsSent / 1024,ServerInstance->stats->statsRecv / 1024); + results.push_back(sn+buffer); + } + break; + + /* stats o */ + case 'o': + for (int i = 0; i < ServerInstance->Config->ConfValueEnum(ServerInstance->Config->config_data, "oper"); i++) + { + char LoginName[MAXBUF]; + char HostName[MAXBUF]; + char OperType[MAXBUF]; + ServerInstance->Config->ConfValue(ServerInstance->Config->config_data, "oper","name", i, LoginName, MAXBUF); + ServerInstance->Config->ConfValue(ServerInstance->Config->config_data, "oper","host", i, HostName, MAXBUF); + ServerInstance->Config->ConfValue(ServerInstance->Config->config_data, "oper","type", i, OperType, MAXBUF); + results.push_back(sn+" 243 "+user->nick+" O "+HostName+" * "+LoginName+" "+OperType+" 0"); + } + break; + + /* stats l (show user I/O stats) */ + case 'l': + results.push_back(sn+" 211 "+user->nick+" :nick[ident@host] sendq cmds_out bytes_out cmds_in bytes_in time_open"); + for (std::vector<userrec*>::iterator n = ServerInstance->local_users.begin(); n != ServerInstance->local_users.end(); n++) + { + userrec* i = *n; + if (ServerInstance->IsNick(i->nick)) + { + results.push_back(sn+" 211 "+user->nick+" "+i->nick+"["+i->ident+"@"+i->dhost+"] "+ConvToStr(i->sendq.length())+" "+ConvToStr(i->cmds_out)+" "+ConvToStr(i->bytes_out)+" "+ConvToStr(i->cmds_in)+" "+ConvToStr(i->bytes_in)+" "+ConvToStr(ServerInstance->Time() - i->age)); + } + } + break; + + /* stats L (show user I/O stats with IP addresses) */ + case 'L': + results.push_back(sn+" 211 "+user->nick+" :nick[ident@ip] sendq cmds_out bytes_out cmds_in bytes_in time_open"); + for (std::vector<userrec*>::iterator n = ServerInstance->local_users.begin(); n != ServerInstance->local_users.end(); n++) + { + userrec* i = *n; + if (ServerInstance->IsNick(i->nick)) + { + results.push_back(sn+" 211 "+user->nick+" "+i->nick+"["+i->ident+"@"+i->GetIPString()+"] "+ConvToStr(i->sendq.length())+" "+ConvToStr(i->cmds_out)+" "+ConvToStr(i->bytes_out)+" "+ConvToStr(i->cmds_in)+" "+ConvToStr(i->bytes_in)+" "+ConvToStr(ServerInstance->Time() - i->age)); + } + } + break; + + /* stats u (show server uptime) */ + case 'u': + { + time_t current_time = 0; + current_time = ServerInstance->Time(); + time_t server_uptime = current_time - ServerInstance->startup_time; + struct tm* stime; + stime = gmtime(&server_uptime); + /* i dont know who the hell would have an ircd running for over a year nonstop, but + * Craig suggested this, and it seemed a good idea so in it went */ + if (stime->tm_year > 70) + { + char buffer[MAXBUF]; + snprintf(buffer,MAXBUF," 242 %s :Server up %d years, %d days, %.2d:%.2d:%.2d",user->nick,(stime->tm_year-70),stime->tm_yday,stime->tm_hour,stime->tm_min,stime->tm_sec); + results.push_back(sn+buffer); + } + else + { + char buffer[MAXBUF]; + snprintf(buffer,MAXBUF," 242 %s :Server up %d days, %.2d:%.2d:%.2d",user->nick,stime->tm_yday,stime->tm_hour,stime->tm_min,stime->tm_sec); + results.push_back(sn+buffer); + } + } + break; + + default: + break; + } + + results.push_back(sn+" 219 "+user->nick+" "+statschar+" :End of /STATS report"); + ServerInstance->SNO->WriteToSnoMask('t',"%s '%c' requested by %s (%s@%s)",(!strcmp(user->server,ServerInstance->Config->ServerName) ? "Stats" : "Remote stats"),statschar,user->nick,user->ident,user->host); + + return; +} + diff --git a/src/cmd_summon.cpp b/src/cmd_summon.cpp index 134ac9f2f..520bdf090 100644 --- a/src/cmd_summon.cpp +++ b/src/cmd_summon.cpp @@ -1 +1,27 @@ -/* +------------------------------------+
* | 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 "users.h"
#include "commands/cmd_summon.h"
extern "C" DllExport command_t* init_command(InspIRCd* Instance)
{
return new cmd_summon(Instance);
}
CmdResult cmd_summon::Handle (const char** parameters, int pcnt, userrec *user)
{
user->WriteServ("445 %s :SUMMON has been disabled (depreciated command)",user->nick);
return CMD_FAILURE;
}
\ No newline at end of file +/* +------------------------------------+ + * | 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 "users.h" +#include "commands/cmd_summon.h" + +extern "C" DllExport command_t* init_command(InspIRCd* Instance) +{ + return new cmd_summon(Instance); +} + +CmdResult cmd_summon::Handle (const char** parameters, int pcnt, userrec *user) +{ + user->WriteServ("445 %s :SUMMON has been disabled (depreciated command)",user->nick); + return CMD_FAILURE; +} diff --git a/src/cmd_time.cpp b/src/cmd_time.cpp index 272a0ce82..cd0f0e1c7 100644 --- a/src/cmd_time.cpp +++ b/src/cmd_time.cpp @@ -1 +1,40 @@ -/* +------------------------------------+
* | 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 "users.h"
#include "commands/cmd_time.h"
extern "C" DllExport command_t* init_command(InspIRCd* Instance)
{
return new cmd_time(Instance);
}
CmdResult cmd_time::Handle (const char** parameters, int pcnt, userrec *user)
{
struct tm* timeinfo;
time_t local = ServerInstance->Time();
timeinfo = localtime(&local);
char tms[26];
snprintf(tms,26,"%s",asctime(timeinfo));
tms[24] = 0;
user->WriteServ("391 %s %s :%s",user->nick,ServerInstance->Config->ServerName,tms);
return CMD_SUCCESS;
}
\ No newline at end of file +/* +------------------------------------+ + * | 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 "users.h" +#include "commands/cmd_time.h" + + + +extern "C" DllExport command_t* init_command(InspIRCd* Instance) +{ + return new cmd_time(Instance); +} + +CmdResult cmd_time::Handle (const char** parameters, int pcnt, userrec *user) +{ + struct tm* timeinfo; + time_t local = ServerInstance->Time(); + + timeinfo = localtime(&local); + + char tms[26]; + snprintf(tms,26,"%s",asctime(timeinfo)); + tms[24] = 0; + + user->WriteServ("391 %s %s :%s",user->nick,ServerInstance->Config->ServerName,tms); + + return CMD_SUCCESS; +} diff --git a/src/cmd_topic.cpp b/src/cmd_topic.cpp index 82f21b24e..741558282 100644 --- a/src/cmd_topic.cpp +++ b/src/cmd_topic.cpp @@ -1 +1,118 @@ -/* +------------------------------------+
* | 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 "users.h"
#include "modules.h"
#include "commands/cmd_topic.h"
extern "C" DllExport command_t* init_command(InspIRCd* Instance)
{
return new cmd_topic(Instance);
}
CmdResult cmd_topic::Handle (const char** parameters, int pcnt, userrec *user)
{
chanrec* Ptr;
if (pcnt == 1)
{
Ptr = ServerInstance->FindChan(parameters[0]);
if (Ptr)
{
if ((Ptr->modes[CM_SECRET]) && (!Ptr->HasUser(user)))
{
user->WriteServ("401 %s %s :No such nick/channel",user->nick, Ptr->name);
return CMD_FAILURE;
}
if (Ptr->topicset)
{
user->WriteServ("332 %s %s :%s", user->nick, Ptr->name, Ptr->topic);
user->WriteServ("333 %s %s %s %d", user->nick, Ptr->name, Ptr->setby, Ptr->topicset);
}
else
{
user->WriteServ("331 %s %s :No topic is set.", user->nick, Ptr->name);
}
}
else
{
user->WriteServ("401 %s %s :No such nick/channel",user->nick, parameters[0]);
return CMD_FAILURE;
}
return CMD_SUCCESS;
}
else if (pcnt>1)
{
Ptr = ServerInstance->FindChan(parameters[0]);
if (Ptr)
{
if (IS_LOCAL(user))
{
if (!Ptr->HasUser(user))
{
user->WriteServ("442 %s %s :You're not on that channel!",user->nick, Ptr->name);
return CMD_FAILURE;
}
if ((Ptr->modes[CM_TOPICLOCK]) && (Ptr->GetStatus(user) < STATUS_HOP))
{
user->WriteServ("482 %s %s :You must be at least a half-operator to change the topic on this channel", user->nick, Ptr->name);
return CMD_FAILURE;
}
}
char topic[MAXTOPIC];
if (IS_LOCAL(user))
{
/* XXX: we need two string copies for a local topic, because we cant
* let a module see the topic as longer than it actually is
*/
int MOD_RESULT = 0;
strlcpy(topic,parameters[1],MAXTOPIC-1);
FOREACH_RESULT(I_OnLocalTopicChange,OnLocalTopicChange(user,Ptr,topic));
if (MOD_RESULT)
return CMD_FAILURE;
strlcpy(Ptr->topic,topic,MAXTOPIC-1);
}
else
{
/* Sneaky shortcut, one string copy for a remote topic */
strlcpy(Ptr->topic, parameters[1], MAXTOPIC-1);
}
if (ServerInstance->Config->FullHostInTopic)
strlcpy(Ptr->setby,user->GetFullHost(),127);
else
strlcpy(Ptr->setby,user->nick,127);
Ptr->topicset = ServerInstance->Time();
Ptr->WriteChannel(user, "TOPIC %s :%s", Ptr->name, Ptr->topic);
if (IS_LOCAL(user))
/* We know 'topic' will contain valid data here */
FOREACH_MOD(I_OnPostLocalTopicChange,OnPostLocalTopicChange(user, Ptr, topic));
}
else
{
user->WriteServ("401 %s %s :No such nick/channel",user->nick, parameters[0]);
return CMD_FAILURE;
}
}
return CMD_SUCCESS;
}
\ No newline at end of file +/* +------------------------------------+ + * | 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 "users.h" +#include "modules.h" +#include "commands/cmd_topic.h" + + +extern "C" DllExport command_t* init_command(InspIRCd* Instance) +{ + return new cmd_topic(Instance); +} + +CmdResult cmd_topic::Handle (const char** parameters, int pcnt, userrec *user) +{ + chanrec* Ptr; + + if (pcnt == 1) + { + Ptr = ServerInstance->FindChan(parameters[0]); + if (Ptr) + { + if ((Ptr->modes[CM_SECRET]) && (!Ptr->HasUser(user))) + { + user->WriteServ("401 %s %s :No such nick/channel",user->nick, Ptr->name); + return CMD_FAILURE; + } + if (Ptr->topicset) + { + user->WriteServ("332 %s %s :%s", user->nick, Ptr->name, Ptr->topic); + user->WriteServ("333 %s %s %s %d", user->nick, Ptr->name, Ptr->setby, Ptr->topicset); + } + else + { + user->WriteServ("331 %s %s :No topic is set.", user->nick, Ptr->name); + } + } + else + { + user->WriteServ("401 %s %s :No such nick/channel",user->nick, parameters[0]); + return CMD_FAILURE; + } + return CMD_SUCCESS; + } + else if (pcnt>1) + { + Ptr = ServerInstance->FindChan(parameters[0]); + if (Ptr) + { + if (IS_LOCAL(user)) + { + if (!Ptr->HasUser(user)) + { + user->WriteServ("442 %s %s :You're not on that channel!",user->nick, Ptr->name); + return CMD_FAILURE; + } + if ((Ptr->modes[CM_TOPICLOCK]) && (Ptr->GetStatus(user) < STATUS_HOP)) + { + user->WriteServ("482 %s %s :You must be at least a half-operator to change the topic on this channel", user->nick, Ptr->name); + return CMD_FAILURE; + } + } + + char topic[MAXTOPIC]; + + if (IS_LOCAL(user)) + { + /* XXX: we need two string copies for a local topic, because we cant + * let a module see the topic as longer than it actually is + */ + int MOD_RESULT = 0; + + strlcpy(topic,parameters[1],MAXTOPIC-1); + FOREACH_RESULT(I_OnLocalTopicChange,OnLocalTopicChange(user,Ptr,topic)); + if (MOD_RESULT) + return CMD_FAILURE; + + strlcpy(Ptr->topic,topic,MAXTOPIC-1); + } + else + { + /* Sneaky shortcut, one string copy for a remote topic */ + strlcpy(Ptr->topic, parameters[1], MAXTOPIC-1); + } + + if (ServerInstance->Config->FullHostInTopic) + strlcpy(Ptr->setby,user->GetFullHost(),127); + else + strlcpy(Ptr->setby,user->nick,127); + + Ptr->topicset = ServerInstance->Time(); + Ptr->WriteChannel(user, "TOPIC %s :%s", Ptr->name, Ptr->topic); + + if (IS_LOCAL(user)) + /* We know 'topic' will contain valid data here */ + FOREACH_MOD(I_OnPostLocalTopicChange,OnPostLocalTopicChange(user, Ptr, topic)); + } + else + { + user->WriteServ("401 %s %s :No such nick/channel",user->nick, parameters[0]); + return CMD_FAILURE; + } + } + return CMD_SUCCESS; +} + diff --git a/src/cmd_trace.cpp b/src/cmd_trace.cpp index 9cb58e8ee..42105df98 100644 --- a/src/cmd_trace.cpp +++ b/src/cmd_trace.cpp @@ -1 +1,46 @@ -/* +------------------------------------+
* | 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 "users.h"
#include "commands/cmd_trace.h"
extern "C" DllExport command_t* init_command(InspIRCd* Instance)
{
return new cmd_trace(Instance);
}
/** XXX: This is crap. someone fix this when you have time, to be more useful.
*/
CmdResult cmd_trace::Handle (const char** parameters, int pcnt, userrec *user)
{
for (user_hash::iterator i = ServerInstance->clientlist->begin(); i != ServerInstance->clientlist->end(); i++)
{
if (i->second->registered == REG_ALL)
{
if (IS_OPER(i->second))
{
user->WriteServ("205 %s :Oper 0 %s",user->nick,i->second->nick);
}
else
{
user->WriteServ("204 %s :User 0 %s",user->nick,i->second->nick);
}
}
else
{
user->WriteServ("203 %s :???? 0 [%s]",user->nick,i->second->host);
}
}
return CMD_SUCCESS;
}
\ No newline at end of file +/* +------------------------------------+ + * | 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 "users.h" +#include "commands/cmd_trace.h" + +extern "C" DllExport command_t* init_command(InspIRCd* Instance) +{ + return new cmd_trace(Instance); +} + +/** XXX: This is crap. someone fix this when you have time, to be more useful. + */ +CmdResult cmd_trace::Handle (const char** parameters, int pcnt, userrec *user) +{ + for (user_hash::iterator i = ServerInstance->clientlist->begin(); i != ServerInstance->clientlist->end(); i++) + { + if (i->second->registered == REG_ALL) + { + if (IS_OPER(i->second)) + { + user->WriteServ("205 %s :Oper 0 %s",user->nick,i->second->nick); + } + else + { + user->WriteServ("204 %s :User 0 %s",user->nick,i->second->nick); + } + } + else + { + user->WriteServ("203 %s :???? 0 [%s]",user->nick,i->second->host); + } + } + return CMD_SUCCESS; +} diff --git a/src/cmd_unloadmodule.cpp b/src/cmd_unloadmodule.cpp index 51192aa7a..44c2133e7 100644 --- a/src/cmd_unloadmodule.cpp +++ b/src/cmd_unloadmodule.cpp @@ -1 +1,39 @@ -/* +------------------------------------+
* | 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 "users.h"
#include "commands/cmd_unloadmodule.h"
extern "C" DllExport command_t* init_command(InspIRCd* Instance)
{
return new cmd_unloadmodule(Instance);
}
CmdResult cmd_unloadmodule::Handle (const char** parameters, int pcnt, userrec *user)
{
if (ServerInstance->UnloadModule(parameters[0]))
{
ServerInstance->WriteOpers("*** MODULE UNLOADED: %s unloaded %s", user->nick, parameters[0]);
user->WriteServ("973 %s %s :Module successfully unloaded.",user->nick, parameters[0]);
}
else
{
user->WriteServ("972 %s %s :Failed to unload module: %s",user->nick, parameters[0],ServerInstance->ModuleError());
return CMD_FAILURE;
}
return CMD_SUCCESS;
}
\ No newline at end of file +/* +------------------------------------+ + * | 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 "users.h" +#include "commands/cmd_unloadmodule.h" + + + +extern "C" DllExport command_t* init_command(InspIRCd* Instance) +{ + return new cmd_unloadmodule(Instance); +} + +CmdResult cmd_unloadmodule::Handle (const char** parameters, int pcnt, userrec *user) +{ + if (ServerInstance->UnloadModule(parameters[0])) + { + ServerInstance->WriteOpers("*** MODULE UNLOADED: %s unloaded %s", user->nick, parameters[0]); + user->WriteServ("973 %s %s :Module successfully unloaded.",user->nick, parameters[0]); + } + else + { + user->WriteServ("972 %s %s :Failed to unload module: %s",user->nick, parameters[0],ServerInstance->ModuleError()); + return CMD_FAILURE; + } + + return CMD_SUCCESS; +} diff --git a/src/cmd_user.cpp b/src/cmd_user.cpp index aa9e3d321..dc224db76 100644 --- a/src/cmd_user.cpp +++ b/src/cmd_user.cpp @@ -1 +1,69 @@ -/* +------------------------------------+
* | 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 "users.h"
#include "commands/cmd_user.h"
extern "C" DllExport command_t* init_command(InspIRCd* Instance)
{
return new cmd_user(Instance);
}
CmdResult cmd_user::Handle (const char** parameters, int pcnt, userrec *user)
{
/* A user may only send the USER command once */
if (!(user->registered & REG_USER))
{
if (!ServerInstance->IsIdent(parameters[0]))
{
/*
* RFC says we must use this numeric, so we do. Let's make it a little more nub friendly though. :)
* -- Craig, and then w00t.
*/
user->WriteServ("461 %s USER :Your username is not valid",user->nick);
return CMD_FAILURE;
}
else
{
/* We're not checking ident, but I'm not sure I like the idea of '~' prefixing.. */
/* XXX - The ident field is IDENTMAX+2 in size to account for +1 for the optional
* ~ character, and +1 for null termination, therefore we can safely use up to
* IDENTMAX here.
*/
strlcpy(user->ident, parameters[0], IDENTMAX);
strlcpy(user->fullname, *parameters[3] ? parameters[3] : "No info", MAXGECOS);
user->registered = (user->registered | REG_USER);
}
}
else
{
user->WriteServ("462 %s :You may not reregister",user->nick);
return CMD_FAILURE;
}
/* parameters 2 and 3 are local and remote hosts, ignored when sent by client connection */
if (user->registered == REG_NICKUSER)
{
int MOD_RESULT = 0;
/* user is registered now, bit 0 = USER command, bit 1 = sent a NICK command */
if (ServerInstance->next_call > ServerInstance->Time() + ServerInstance->Config->dns_timeout)
ServerInstance->next_call = ServerInstance->Time() + ServerInstance->Config->dns_timeout;
FOREACH_RESULT(I_OnUserRegister,OnUserRegister(user));
if (MOD_RESULT > 0)
return CMD_FAILURE;
}
return CMD_SUCCESS;
}
\ No newline at end of file +/* +------------------------------------+ + * | 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 "users.h" +#include "commands/cmd_user.h" + +extern "C" DllExport command_t* init_command(InspIRCd* Instance) +{ + return new cmd_user(Instance); +} + +CmdResult cmd_user::Handle (const char** parameters, int pcnt, userrec *user) +{ + /* A user may only send the USER command once */ + if (!(user->registered & REG_USER)) + { + if (!ServerInstance->IsIdent(parameters[0])) + { + /* + * RFC says we must use this numeric, so we do. Let's make it a little more nub friendly though. :) + * -- Craig, and then w00t. + */ + user->WriteServ("461 %s USER :Your username is not valid",user->nick); + return CMD_FAILURE; + } + else + { + /* We're not checking ident, but I'm not sure I like the idea of '~' prefixing.. */ + /* XXX - The ident field is IDENTMAX+2 in size to account for +1 for the optional + * ~ character, and +1 for null termination, therefore we can safely use up to + * IDENTMAX here. + */ + strlcpy(user->ident, parameters[0], IDENTMAX); + strlcpy(user->fullname, *parameters[3] ? parameters[3] : "No info", MAXGECOS); + user->registered = (user->registered | REG_USER); + } + } + else + { + user->WriteServ("462 %s :You may not reregister",user->nick); + return CMD_FAILURE; + } + /* parameters 2 and 3 are local and remote hosts, ignored when sent by client connection */ + if (user->registered == REG_NICKUSER) + { + int MOD_RESULT = 0; + /* user is registered now, bit 0 = USER command, bit 1 = sent a NICK command */ + if (ServerInstance->next_call > ServerInstance->Time() + ServerInstance->Config->dns_timeout) + ServerInstance->next_call = ServerInstance->Time() + ServerInstance->Config->dns_timeout; + FOREACH_RESULT(I_OnUserRegister,OnUserRegister(user)); + if (MOD_RESULT > 0) + return CMD_FAILURE; + + } + + return CMD_SUCCESS; +} diff --git a/src/cmd_userhost.cpp b/src/cmd_userhost.cpp index c0a2362cd..9e644bdd1 100644 --- a/src/cmd_userhost.cpp +++ b/src/cmd_userhost.cpp @@ -1 +1,63 @@ -/* +------------------------------------+
* | 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 "users.h"
#include "commands/cmd_userhost.h"
extern "C" DllExport command_t* init_command(InspIRCd* Instance)
{
return new cmd_userhost(Instance);
}
CmdResult cmd_userhost::Handle (const char** parameters, int pcnt, userrec *user)
{
std::string retbuf = std::string("302 ") + user->nick + " :";
for (int i = 0; i < pcnt; i++)
{
userrec *u = ServerInstance->FindNick(parameters[i]);
if ((u) && (u->registered == REG_ALL))
{
retbuf = retbuf + u->nick;
if (IS_OPER(u))
{
retbuf = retbuf + "*=+";
}
else
{
retbuf = retbuf + "=+";
}
retbuf = retbuf + u->ident + "@";
if (IS_OPER(user))
{
retbuf = retbuf + u->host;
}
else
{
retbuf = retbuf + u->dhost;
}
retbuf = retbuf + " ";
}
}
user->WriteServ(retbuf);
return CMD_SUCCESS;
}
\ No newline at end of file +/* +------------------------------------+ + * | 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 "users.h" +#include "commands/cmd_userhost.h" + +extern "C" DllExport command_t* init_command(InspIRCd* Instance) +{ + return new cmd_userhost(Instance); +} + +CmdResult cmd_userhost::Handle (const char** parameters, int pcnt, userrec *user) +{ + std::string retbuf = std::string("302 ") + user->nick + " :"; + + + for (int i = 0; i < pcnt; i++) + { + userrec *u = ServerInstance->FindNick(parameters[i]); + + if ((u) && (u->registered == REG_ALL)) + { + retbuf = retbuf + u->nick; + + if (IS_OPER(u)) + { + retbuf = retbuf + "*=+"; + } + else + { + retbuf = retbuf + "=+"; + } + + retbuf = retbuf + u->ident + "@"; + + if (IS_OPER(user)) + { + retbuf = retbuf + u->host; + } + else + { + retbuf = retbuf + u->dhost; + } + + retbuf = retbuf + " "; + } + } + + user->WriteServ(retbuf); + + return CMD_SUCCESS; +} diff --git a/src/cmd_users.cpp b/src/cmd_users.cpp index c3cb02075..97b9b247d 100644 --- a/src/cmd_users.cpp +++ b/src/cmd_users.cpp @@ -1 +1,27 @@ -/* +------------------------------------+
* | 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 "users.h"
#include "commands/cmd_users.h"
extern "C" DllExport command_t* init_command(InspIRCd* Instance)
{
return new cmd_users(Instance);
}
CmdResult cmd_users::Handle (const char** parameters, int pcnt, userrec *user)
{
user->WriteServ("445 %s :USERS has been disabled (depreciated command)",user->nick);
return CMD_FAILURE;
}
\ No newline at end of file +/* +------------------------------------+ + * | 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 "users.h" +#include "commands/cmd_users.h" + +extern "C" DllExport command_t* init_command(InspIRCd* Instance) +{ + return new cmd_users(Instance); +} + +CmdResult cmd_users::Handle (const char** parameters, int pcnt, userrec *user) +{ + user->WriteServ("445 %s :USERS has been disabled (depreciated command)",user->nick); + return CMD_FAILURE; +} diff --git a/src/cmd_version.cpp b/src/cmd_version.cpp index a33dd5bd0..599b7bf75 100644 --- a/src/cmd_version.cpp +++ b/src/cmd_version.cpp @@ -1 +1,31 @@ -/* +------------------------------------+
* | 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 "users.h"
#include "commands/cmd_version.h"
extern "C" DllExport command_t* init_command(InspIRCd* Instance)
{
return new cmd_version(Instance);
}
CmdResult cmd_version::Handle (const char** parameters, int pcnt, userrec *user)
{
user->WriteServ("351 %s :%s",user->nick,ServerInstance->GetVersionString().c_str());
ServerInstance->Config->Send005(user);
return CMD_SUCCESS;
}
\ No newline at end of file +/* +------------------------------------+ + * | 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 "users.h" +#include "commands/cmd_version.h" + + + +extern "C" DllExport command_t* init_command(InspIRCd* Instance) +{ + return new cmd_version(Instance); +} + +CmdResult cmd_version::Handle (const char** parameters, int pcnt, userrec *user) +{ + user->WriteServ("351 %s :%s",user->nick,ServerInstance->GetVersionString().c_str()); + ServerInstance->Config->Send005(user); + return CMD_SUCCESS; +} diff --git a/src/cmd_wallops.cpp b/src/cmd_wallops.cpp index 88c8fccf5..d32b19ebd 100644 --- a/src/cmd_wallops.cpp +++ b/src/cmd_wallops.cpp @@ -1 +1,31 @@ -/* +------------------------------------+
* | 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 "modules.h"
#include "commands/cmd_wallops.h"
extern "C" DllExport command_t* init_command(InspIRCd* Instance)
{
return new cmd_wallops(Instance);
}
CmdResult cmd_wallops::Handle (const char** parameters, int pcnt, userrec *user)
{
user->WriteWallOps(std::string(parameters[0]));
FOREACH_MOD(I_OnWallops,OnWallops(user,parameters[0]));
return CMD_SUCCESS;
}
\ No newline at end of file +/* +------------------------------------+ + * | 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 "modules.h" +#include "commands/cmd_wallops.h" + + + +extern "C" DllExport command_t* init_command(InspIRCd* Instance) +{ + return new cmd_wallops(Instance); +} + +CmdResult cmd_wallops::Handle (const char** parameters, int pcnt, userrec *user) +{ + user->WriteWallOps(std::string(parameters[0])); + FOREACH_MOD(I_OnWallops,OnWallops(user,parameters[0])); + return CMD_SUCCESS; +} diff --git a/src/cmd_who.cpp b/src/cmd_who.cpp index 6054351d9..31e8030f5 100644 --- a/src/cmd_who.cpp +++ b/src/cmd_who.cpp @@ -1 +1,328 @@ -/* +------------------------------------+
* | 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 "users.h"
#include "modules.h"
#include "wildcard.h"
#include "commands/cmd_who.h"
/* get the last 'visible' chan of a user */
static char *getlastchanname(userrec *u)
{
UCListIter i = u->chans.begin();
if (i != u->chans.end())
{
if (!i->first->IsModeSet('s'))
return i->first->name;
}
return "*";
}
bool cmd_who::whomatch(userrec* user, const char* matchtext)
{
bool realhost = false;
bool realname = false;
bool positive = true;
bool metadata = false;
bool ident = false;
bool away = false;
bool port = false;
char* dummy = NULL;
if (user->registered != REG_ALL)
return false;
if (opt_local && !IS_LOCAL(user))
return false;
else if (opt_far && IS_LOCAL(user))
return false;
if (opt_mode)
{
for (const char* n = matchtext; *n; n++)
{
if (*n == '+')
{
positive = true;
continue;
}
else if (*n == '-')
{
positive = false;
continue;
}
if (user->IsModeSet(*n) != positive)
return false;
}
return true;
}
else
{
if (opt_metadata)
metadata = user->GetExt(matchtext, dummy);
else
{
if (opt_realname)
realname = match(user->fullname, matchtext);
else
{
if (opt_showrealhost)
realhost = match(user->host, matchtext);
else
{
if (opt_ident)
ident = match(user->ident, matchtext);
else
{
if (opt_port)
{
irc::portparser portrange(matchtext, false);
long portno = -1;
while ((portno = portrange.GetToken()))
if (portno == user->GetPort())
port = true;
}
else
{
if (opt_away)
away = match(user->awaymsg, matchtext);
}
}
}
}
}
return ((port) || (away) || (ident) || (metadata) || (realname) || (realhost) || (match(user->dhost, matchtext)) || (match(user->nick, matchtext)) || (match(user->server, matchtext)));
}
}
extern "C" DllExport command_t* init_command(InspIRCd* Instance)
{
return new cmd_who(Instance);
}
bool cmd_who::CanView(chanrec* chan, userrec* user)
{
if (!user || !chan)
return false;
/* Execute items in fastest-to-execute first order */
/* Opers see all */
if (IS_OPER(user))
return true;
else if (!chan->IsModeSet('s') && !chan->IsModeSet('p'))
return true;
else if (chan->HasUser(user))
return true;
return false;
}
void cmd_who::SendWhoLine(userrec* user, const std::string &initial, chanrec* ch, userrec* u, std::vector<std::string> &whoresults)
{
std::string lcn = getlastchanname(u);
chanrec* chlast = ServerInstance->FindChan(lcn);
/* Not visible to this user */
if (u->Visibility && !u->Visibility->VisibleTo(user))
return;
std::string wholine = initial + (ch ? ch->name : lcn) + " " + u->ident + " " + (opt_showrealhost ? u->host : u->dhost) + " " +
((*ServerInstance->Config->HideWhoisServer && !IS_OPER(user)) ? ServerInstance->Config->HideWhoisServer : u->server) +
" " + u->nick + " ";
/* away? */
if (IS_AWAY(u))
{
wholine.append("G");
}
else
{
wholine.append("H");
}
/* oper? */
if (IS_OPER(u))
{
wholine.append("*");
}
wholine = wholine + (ch ? ch->GetPrefixChar(u) : (chlast ? chlast->GetPrefixChar(u) : "")) + " :0 " + u->fullname;
whoresults.push_back(wholine);
}
CmdResult cmd_who::Handle (const char** parameters, int pcnt, userrec *user)
{
/*
* XXX - RFC says:
* The <name> passed to WHO is matched against users' host, server, real
* name and nickname
* Currently, we support WHO #chan, WHO nick, WHO 0, WHO *, and the addition of a 'o' flag, as per RFC.
*/
/* WHO options */
opt_viewopersonly = false;
opt_showrealhost = false;
opt_unlimit = false;
opt_realname = false;
opt_mode = false;
opt_ident = false;
opt_metadata = false;
opt_port = false;
opt_away = false;
opt_local = false;
opt_far = false;
chanrec *ch = NULL;
std::vector<std::string> whoresults;
std::string initial = "352 " + std::string(user->nick) + " ";
const char* matchtext = NULL;
/* Change '0' into '*' so the wildcard matcher can grok it */
matchtext = parameters[0];
if (!strcmp(matchtext,"0"))
matchtext = "*";
if (pcnt > 1)
{
/* parse flags */
const char *iter = parameters[1];
while (*iter)
{
switch (*iter)
{
case 'o':
opt_viewopersonly = true;
break;
case 'h':
if (IS_OPER(user))
opt_showrealhost = true;
break;
case 'u':
if (IS_OPER(user))
opt_unlimit = true;
break;
case 'r':
opt_realname = true;
break;
case 'm':
opt_mode = true;
break;
case 'M':
opt_metadata = true;
break;
case 'i':
opt_ident = true;
break;
case 'p':
opt_port = true;
break;
case 'a':
opt_away = true;
break;
case 'l':
opt_local = true;
break;
case 'f':
opt_far = true;
break;
}
*iter++;
}
}
/* who on a channel? */
ch = ServerInstance->FindChan(matchtext);
if (ch)
{
if (CanView(ch,user))
{
bool inside = ch->HasUser(user);
/* who on a channel. */
CUList *cu = ch->GetUsers();
for (CUList::iterator i = cu->begin(); i != cu->end(); i++)
{
/* opers only, please */
if (opt_viewopersonly && !IS_OPER(i->first))
continue;
/* If we're not inside the channel, hide +i users */
if (i->first->IsModeSet('i') && !inside)
continue;
SendWhoLine(user, initial, ch, i->first, whoresults);
}
}
}
else
{
/* Match against wildcard of nick, server or host */
if (opt_viewopersonly)
{
/* Showing only opers */
for (std::vector<userrec*>::iterator i = ServerInstance->all_opers.begin(); i != ServerInstance->all_opers.end(); i++)
{
userrec* oper = *i;
if (whomatch(oper, matchtext))
{
if ((!oper->IsModeSet('i')) && (!IS_OPER(user)))
continue;
SendWhoLine(user, initial, NULL, oper, whoresults);
}
}
}
else
{
for (user_hash::iterator i = ServerInstance->clientlist->begin(); i != ServerInstance->clientlist->end(); i++)
{
if (whomatch(i->second, matchtext))
{
if ((i->second->IsModeSet('i')) && (!IS_OPER(user)))
continue;
SendWhoLine(user, initial, NULL, i->second, whoresults);
}
}
}
}
/* Send the results out */
if ((ServerInstance->Config->MaxWhoResults && (whoresults.size() <= (size_t)ServerInstance->Config->MaxWhoResults)) || opt_unlimit)
{
for (std::vector<std::string>::const_iterator n = whoresults.begin(); n != whoresults.end(); n++)
user->WriteServ(*n);
user->WriteServ("315 %s %s :End of /WHO list.",user->nick, *parameters[0] ? parameters[0] : "*");
return CMD_SUCCESS;
}
else
{
/* BZZT! Too many results. */
user->WriteServ("315 %s %s :Too many results",user->nick, parameters[0]);
return CMD_FAILURE;
}
}
\ No newline at end of file +/* +------------------------------------+ + * | 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 "users.h" +#include "modules.h" +#include "wildcard.h" +#include "commands/cmd_who.h" + +/* get the last 'visible' chan of a user */ +static char *getlastchanname(userrec *u) +{ + UCListIter i = u->chans.begin(); + if (i != u->chans.end()) + { + if (!i->first->IsModeSet('s')) + return i->first->name; + } + + return "*"; +} + +bool cmd_who::whomatch(userrec* user, const char* matchtext) +{ + bool realhost = false; + bool realname = false; + bool positive = true; + bool metadata = false; + bool ident = false; + bool away = false; + bool port = false; + char* dummy = NULL; + + if (user->registered != REG_ALL) + return false; + + if (opt_local && !IS_LOCAL(user)) + return false; + else if (opt_far && IS_LOCAL(user)) + return false; + + if (opt_mode) + { + for (const char* n = matchtext; *n; n++) + { + if (*n == '+') + { + positive = true; + continue; + } + else if (*n == '-') + { + positive = false; + continue; + } + if (user->IsModeSet(*n) != positive) + return false; + } + return true; + } + else + { + + if (opt_metadata) + metadata = user->GetExt(matchtext, dummy); + else + { + if (opt_realname) + realname = match(user->fullname, matchtext); + else + { + if (opt_showrealhost) + realhost = match(user->host, matchtext); + else + { + if (opt_ident) + ident = match(user->ident, matchtext); + else + { + if (opt_port) + { + irc::portparser portrange(matchtext, false); + long portno = -1; + while ((portno = portrange.GetToken())) + if (portno == user->GetPort()) + port = true; + } + else + { + if (opt_away) + away = match(user->awaymsg, matchtext); + } + } + } + } + } + return ((port) || (away) || (ident) || (metadata) || (realname) || (realhost) || (match(user->dhost, matchtext)) || (match(user->nick, matchtext)) || (match(user->server, matchtext))); + } +} + + + +extern "C" DllExport command_t* init_command(InspIRCd* Instance) +{ + return new cmd_who(Instance); +} + +bool cmd_who::CanView(chanrec* chan, userrec* user) +{ + if (!user || !chan) + return false; + + /* Execute items in fastest-to-execute first order */ + + /* Opers see all */ + if (IS_OPER(user)) + return true; + else if (!chan->IsModeSet('s') && !chan->IsModeSet('p')) + return true; + else if (chan->HasUser(user)) + return true; + + return false; +} + +void cmd_who::SendWhoLine(userrec* user, const std::string &initial, chanrec* ch, userrec* u, std::vector<std::string> &whoresults) +{ + std::string lcn = getlastchanname(u); + chanrec* chlast = ServerInstance->FindChan(lcn); + + /* Not visible to this user */ + if (u->Visibility && !u->Visibility->VisibleTo(user)) + return; + + std::string wholine = initial + (ch ? ch->name : lcn) + " " + u->ident + " " + (opt_showrealhost ? u->host : u->dhost) + " " + + ((*ServerInstance->Config->HideWhoisServer && !IS_OPER(user)) ? ServerInstance->Config->HideWhoisServer : u->server) + + " " + u->nick + " "; + + /* away? */ + if (IS_AWAY(u)) + { + wholine.append("G"); + } + else + { + wholine.append("H"); + } + + /* oper? */ + if (IS_OPER(u)) + { + wholine.append("*"); + } + + wholine = wholine + (ch ? ch->GetPrefixChar(u) : (chlast ? chlast->GetPrefixChar(u) : "")) + " :0 " + u->fullname; + whoresults.push_back(wholine); +} + +CmdResult cmd_who::Handle (const char** parameters, int pcnt, userrec *user) +{ + /* + * XXX - RFC says: + * The <name> passed to WHO is matched against users' host, server, real + * name and nickname + * Currently, we support WHO #chan, WHO nick, WHO 0, WHO *, and the addition of a 'o' flag, as per RFC. + */ + + /* WHO options */ + opt_viewopersonly = false; + opt_showrealhost = false; + opt_unlimit = false; + opt_realname = false; + opt_mode = false; + opt_ident = false; + opt_metadata = false; + opt_port = false; + opt_away = false; + opt_local = false; + opt_far = false; + + chanrec *ch = NULL; + std::vector<std::string> whoresults; + std::string initial = "352 " + std::string(user->nick) + " "; + + const char* matchtext = NULL; + + /* Change '0' into '*' so the wildcard matcher can grok it */ + matchtext = parameters[0]; + if (!strcmp(matchtext,"0")) + matchtext = "*"; + + if (pcnt > 1) + { + /* parse flags */ + const char *iter = parameters[1]; + + while (*iter) + { + switch (*iter) + { + case 'o': + opt_viewopersonly = true; + break; + case 'h': + if (IS_OPER(user)) + opt_showrealhost = true; + break; + case 'u': + if (IS_OPER(user)) + opt_unlimit = true; + break; + case 'r': + opt_realname = true; + break; + case 'm': + opt_mode = true; + break; + case 'M': + opt_metadata = true; + break; + case 'i': + opt_ident = true; + break; + case 'p': + opt_port = true; + break; + case 'a': + opt_away = true; + break; + case 'l': + opt_local = true; + break; + case 'f': + opt_far = true; + break; + } + + *iter++; + } + } + + + /* who on a channel? */ + ch = ServerInstance->FindChan(matchtext); + + if (ch) + { + if (CanView(ch,user)) + { + bool inside = ch->HasUser(user); + + /* who on a channel. */ + CUList *cu = ch->GetUsers(); + + for (CUList::iterator i = cu->begin(); i != cu->end(); i++) + { + /* opers only, please */ + if (opt_viewopersonly && !IS_OPER(i->first)) + continue; + + /* If we're not inside the channel, hide +i users */ + if (i->first->IsModeSet('i') && !inside) + continue; + + SendWhoLine(user, initial, ch, i->first, whoresults); + } + } + } + else + { + /* Match against wildcard of nick, server or host */ + + if (opt_viewopersonly) + { + /* Showing only opers */ + for (std::vector<userrec*>::iterator i = ServerInstance->all_opers.begin(); i != ServerInstance->all_opers.end(); i++) + { + userrec* oper = *i; + + if (whomatch(oper, matchtext)) + { + if ((!oper->IsModeSet('i')) && (!IS_OPER(user))) + continue; + + SendWhoLine(user, initial, NULL, oper, whoresults); + } + } + } + else + { + for (user_hash::iterator i = ServerInstance->clientlist->begin(); i != ServerInstance->clientlist->end(); i++) + { + if (whomatch(i->second, matchtext)) + { + if ((i->second->IsModeSet('i')) && (!IS_OPER(user))) + continue; + + SendWhoLine(user, initial, NULL, i->second, whoresults); + } + } + } + } + /* Send the results out */ + if ((ServerInstance->Config->MaxWhoResults && (whoresults.size() <= (size_t)ServerInstance->Config->MaxWhoResults)) || opt_unlimit) + { + for (std::vector<std::string>::const_iterator n = whoresults.begin(); n != whoresults.end(); n++) + user->WriteServ(*n); + user->WriteServ("315 %s %s :End of /WHO list.",user->nick, *parameters[0] ? parameters[0] : "*"); + return CMD_SUCCESS; + } + else + { + /* BZZT! Too many results. */ + user->WriteServ("315 %s %s :Too many results",user->nick, parameters[0]); + return CMD_FAILURE; + } +} diff --git a/src/cmd_whois.cpp b/src/cmd_whois.cpp index 3797efeaa..897ec60ac 100644 --- a/src/cmd_whois.cpp +++ b/src/cmd_whois.cpp @@ -1 +1,144 @@ -/* +------------------------------------+
* | 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 "users.h"
#include "modules.h"
#include "commands/cmd_whois.h"
#include "hashcomp.h"
void do_whois(InspIRCd* ServerInstance, userrec* user, userrec* dest,unsigned long signon, unsigned long idle, const char* nick)
{
if (dest->Visibility && !dest->Visibility->VisibleTo(user))
{
ServerInstance->SendWhoisLine(user, dest, 401, "%s %s :No such nick/channel",user->nick, *nick ? nick : "*");
ServerInstance->SendWhoisLine(user, dest, 318, "%s %s :End of /WHOIS list.",user->nick, *nick ? nick : "*");
return;
}
if (dest->registered == REG_ALL)
{
ServerInstance->SendWhoisLine(user, dest, 311, "%s %s %s %s * :%s",user->nick, dest->nick, dest->ident, dest->dhost, dest->fullname);
if (user == dest || IS_OPER(user))
{
ServerInstance->SendWhoisLine(user, dest, 378, "%s %s :is connecting from %s@%s %s", user->nick, dest->nick, dest->ident, dest->host, dest->GetIPString());
}
std::string cl = dest->ChannelList(user);
if (cl.length())
{
if (cl.length() > 400)
{
user->SplitChanList(dest,cl);
}
else
{
ServerInstance->SendWhoisLine(user, dest, 319, "%s %s :%s",user->nick, dest->nick, cl.c_str());
}
}
if (*ServerInstance->Config->HideWhoisServer && !IS_OPER(user))
{
ServerInstance->SendWhoisLine(user, dest, 312, "%s %s %s :%s",user->nick, dest->nick, ServerInstance->Config->HideWhoisServer, ServerInstance->Config->Network);
}
else
{
ServerInstance->SendWhoisLine(user, dest, 312, "%s %s %s :%s",user->nick, dest->nick, dest->server, ServerInstance->GetServerDescription(dest->server).c_str());
}
if (IS_AWAY(dest))
{
ServerInstance->SendWhoisLine(user, dest, 301, "%s %s :%s",user->nick, dest->nick, dest->awaymsg);
}
if (IS_OPER(dest))
{
ServerInstance->SendWhoisLine(user, dest, 313, "%s %s :is %s %s on %s",user->nick, dest->nick, (strchr("AEIOUaeiou",*dest->oper) ? "an" : "a"),irc::Spacify(dest->oper), ServerInstance->Config->Network);
}
FOREACH_MOD(I_OnWhois,OnWhois(user,dest));
/*
* We only send these if we've been provided them. That is, if hidewhois is turned off, and user is local, or
* if remote whois is queried, too. This is to keep the user hidden, and also since you can't reliably tell remote time. -- w00t
*/
if ((idle) || (signon))
{
ServerInstance->SendWhoisLine(user, dest, 317, "%s %s %d %d :seconds idle, signon time",user->nick, dest->nick, idle, signon);
}
ServerInstance->SendWhoisLine(user, dest, 318, "%s %s :End of /WHOIS list.",user->nick, dest->nick);
}
else
{
ServerInstance->SendWhoisLine(user, dest, 401, "%s %s :No such nick/channel",user->nick, *nick ? nick : "*");
ServerInstance->SendWhoisLine(user, dest, 318, "%s %s :End of /WHOIS list.",user->nick, *nick ? nick : "*");
}
}
extern "C" DllExport command_t* init_command(InspIRCd* Instance)
{
return new cmd_whois(Instance);
}
CmdResult cmd_whois::Handle (const char** parameters, int pcnt, userrec *user)
{
userrec *dest;
int userindex = 0;
unsigned long idle = 0, signon = 0;
if (ServerInstance->Parser->LoopCall(user, this, parameters, pcnt, 0))
return CMD_SUCCESS;
/*
* If 2 paramters are specified (/whois nick nick), ignore the first one like spanningtree
* does, and use the second one, otherwise, use the only paramter. -- djGrrr
*/
if (pcnt > 1)
userindex = 1;
dest = ServerInstance->FindNick(parameters[userindex]);
if (dest)
{
/*
* Okay. Umpteenth attempt at doing this, so let's re-comment...
* For local users (/w localuser), we show idletime if hidewhois is disabled
* For local users (/w localuser localuser), we always show idletime, hence pcnt > 1 check.
* For remote users (/w remoteuser), we do NOT show idletime
* For remote users (/w remoteuser remoteuser), spanningtree will handle calling do_whois, so we can ignore this case.
* Thanks to djGrrr for not being impatient while I have a crap day coding. :p -- w00t
*/
if (IS_LOCAL(dest) && (!*ServerInstance->Config->HideWhoisServer || pcnt > 1))
{
idle = abs((dest->idle_lastmsg)-ServerInstance->Time());
signon = dest->signon;
}
do_whois(this->ServerInstance, user,dest,signon,idle,parameters[userindex]);
}
else
{
/* no such nick/channel */
user->WriteServ("401 %s %s :No such nick/channel",user->nick, *parameters[userindex] ? parameters[userindex] : "*");
user->WriteServ("318 %s %s :End of /WHOIS list.",user->nick, *parameters[userindex] ? parameters[userindex] : "*");
return CMD_FAILURE;
}
return CMD_SUCCESS;
}
\ No newline at end of file +/* +------------------------------------+ + * | 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 "users.h" +#include "modules.h" +#include "commands/cmd_whois.h" +#include "hashcomp.h" + +void do_whois(InspIRCd* ServerInstance, userrec* user, userrec* dest,unsigned long signon, unsigned long idle, const char* nick) +{ + if (dest->Visibility && !dest->Visibility->VisibleTo(user)) + { + ServerInstance->SendWhoisLine(user, dest, 401, "%s %s :No such nick/channel",user->nick, *nick ? nick : "*"); + ServerInstance->SendWhoisLine(user, dest, 318, "%s %s :End of /WHOIS list.",user->nick, *nick ? nick : "*"); + return; + } + + if (dest->registered == REG_ALL) + { + ServerInstance->SendWhoisLine(user, dest, 311, "%s %s %s %s * :%s",user->nick, dest->nick, dest->ident, dest->dhost, dest->fullname); + if (user == dest || IS_OPER(user)) + { + ServerInstance->SendWhoisLine(user, dest, 378, "%s %s :is connecting from %s@%s %s", user->nick, dest->nick, dest->ident, dest->host, dest->GetIPString()); + } + + std::string cl = dest->ChannelList(user); + + if (cl.length()) + { + if (cl.length() > 400) + { + user->SplitChanList(dest,cl); + } + else + { + ServerInstance->SendWhoisLine(user, dest, 319, "%s %s :%s",user->nick, dest->nick, cl.c_str()); + } + } + if (*ServerInstance->Config->HideWhoisServer && !IS_OPER(user)) + { + ServerInstance->SendWhoisLine(user, dest, 312, "%s %s %s :%s",user->nick, dest->nick, ServerInstance->Config->HideWhoisServer, ServerInstance->Config->Network); + } + else + { + ServerInstance->SendWhoisLine(user, dest, 312, "%s %s %s :%s",user->nick, dest->nick, dest->server, ServerInstance->GetServerDescription(dest->server).c_str()); + } + + if (IS_AWAY(dest)) + { + ServerInstance->SendWhoisLine(user, dest, 301, "%s %s :%s",user->nick, dest->nick, dest->awaymsg); + } + + if (IS_OPER(dest)) + { + ServerInstance->SendWhoisLine(user, dest, 313, "%s %s :is %s %s on %s",user->nick, dest->nick, (strchr("AEIOUaeiou",*dest->oper) ? "an" : "a"),irc::Spacify(dest->oper), ServerInstance->Config->Network); + } + + FOREACH_MOD(I_OnWhois,OnWhois(user,dest)); + + /* + * We only send these if we've been provided them. That is, if hidewhois is turned off, and user is local, or + * if remote whois is queried, too. This is to keep the user hidden, and also since you can't reliably tell remote time. -- w00t + */ + if ((idle) || (signon)) + { + ServerInstance->SendWhoisLine(user, dest, 317, "%s %s %d %d :seconds idle, signon time",user->nick, dest->nick, idle, signon); + } + + ServerInstance->SendWhoisLine(user, dest, 318, "%s %s :End of /WHOIS list.",user->nick, dest->nick); + } + else + { + ServerInstance->SendWhoisLine(user, dest, 401, "%s %s :No such nick/channel",user->nick, *nick ? nick : "*"); + ServerInstance->SendWhoisLine(user, dest, 318, "%s %s :End of /WHOIS list.",user->nick, *nick ? nick : "*"); + } +} + + + +extern "C" DllExport command_t* init_command(InspIRCd* Instance) +{ + return new cmd_whois(Instance); +} + +CmdResult cmd_whois::Handle (const char** parameters, int pcnt, userrec *user) +{ + userrec *dest; + int userindex = 0; + unsigned long idle = 0, signon = 0; + + if (ServerInstance->Parser->LoopCall(user, this, parameters, pcnt, 0)) + return CMD_SUCCESS; + + + /* + * If 2 paramters are specified (/whois nick nick), ignore the first one like spanningtree + * does, and use the second one, otherwise, use the only paramter. -- djGrrr + */ + if (pcnt > 1) + userindex = 1; + + dest = ServerInstance->FindNick(parameters[userindex]); + + if (dest) + { + /* + * Okay. Umpteenth attempt at doing this, so let's re-comment... + * For local users (/w localuser), we show idletime if hidewhois is disabled + * For local users (/w localuser localuser), we always show idletime, hence pcnt > 1 check. + * For remote users (/w remoteuser), we do NOT show idletime + * For remote users (/w remoteuser remoteuser), spanningtree will handle calling do_whois, so we can ignore this case. + * Thanks to djGrrr for not being impatient while I have a crap day coding. :p -- w00t + */ + if (IS_LOCAL(dest) && (!*ServerInstance->Config->HideWhoisServer || pcnt > 1)) + { + idle = abs((dest->idle_lastmsg)-ServerInstance->Time()); + signon = dest->signon; + } + + do_whois(this->ServerInstance, user,dest,signon,idle,parameters[userindex]); + } + else + { + /* no such nick/channel */ + user->WriteServ("401 %s %s :No such nick/channel",user->nick, *parameters[userindex] ? parameters[userindex] : "*"); + user->WriteServ("318 %s %s :End of /WHOIS list.",user->nick, *parameters[userindex] ? parameters[userindex] : "*"); + return CMD_FAILURE; + } + + return CMD_SUCCESS; +} + diff --git a/src/cmd_whowas.cpp b/src/cmd_whowas.cpp index aab457243..2d504c47c 100644 --- a/src/cmd_whowas.cpp +++ b/src/cmd_whowas.cpp @@ -1 +1,341 @@ -/* +------------------------------------+
* | 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 "users.h"
#include "commands/cmd_whowas.h"
WhoWasMaintainTimer * timer;
extern "C" DllExport command_t* init_command(InspIRCd* Instance)
{
return new cmd_whowas(Instance);
}
cmd_whowas::cmd_whowas(InspIRCd* Instance)
: command_t(Instance, "WHOWAS", 0, 1)
{
syntax = "<nick>{,<nick>}";
timer = new WhoWasMaintainTimer(Instance, 3600);
Instance->Timers->AddTimer(timer);
}
CmdResult cmd_whowas::Handle (const char** parameters, int pcnt, userrec* user)
{
/* if whowas disabled in config */
if (ServerInstance->Config->WhoWasGroupSize == 0 || ServerInstance->Config->WhoWasMaxGroups == 0)
{
user->WriteServ("421 %s %s :This command has been disabled.",user->nick,command.c_str());
return CMD_FAILURE;
}
whowas_users::iterator i = whowas.find(parameters[0]);
if (i == whowas.end())
{
user->WriteServ("406 %s %s :There was no such nickname",user->nick,parameters[0]);
user->WriteServ("369 %s %s :End of WHOWAS",user->nick,parameters[0]);
return CMD_FAILURE;
}
else
{
whowas_set* grp = i->second;
if (grp->size())
{
for (whowas_set::iterator ux = grp->begin(); ux != grp->end(); ux++)
{
WhoWasGroup* u = *ux;
time_t rawtime = u->signon;
tm *timeinfo;
char b[MAXBUF];
timeinfo = localtime(&rawtime);
/* XXX - 'b' could be only 25 chars long and then strlcpy() would terminate it for us too? */
strlcpy(b,asctime(timeinfo),MAXBUF);
b[24] = 0;
user->WriteServ("314 %s %s %s %s * :%s",user->nick,parameters[0],u->ident,u->dhost,u->gecos);
if (IS_OPER(user))
user->WriteServ("379 %s %s :was connecting from *@%s", user->nick, parameters[0], u->host);
if (*ServerInstance->Config->HideWhoisServer && !IS_OPER(user))
user->WriteServ("312 %s %s %s :%s",user->nick,parameters[0], ServerInstance->Config->HideWhoisServer, b);
else
user->WriteServ("312 %s %s %s :%s",user->nick,parameters[0], u->server, b);
}
}
else
{
user->WriteServ("406 %s %s :There was no such nickname",user->nick,parameters[0]);
user->WriteServ("369 %s %s :End of WHOWAS",user->nick,parameters[0]);
return CMD_FAILURE;
}
}
user->WriteServ("369 %s %s :End of WHOWAS",user->nick,parameters[0]);
return CMD_SUCCESS;
}
CmdResult cmd_whowas::HandleInternal(const unsigned int id, const std::deque<classbase*> ¶meters)
{
switch (id)
{
case WHOWAS_ADD:
AddToWhoWas((userrec*)parameters[0]);
break;
case WHOWAS_STATS:
GetStats((Extensible*)parameters[0]);
break;
case WHOWAS_PRUNE:
PruneWhoWas(ServerInstance->Time());
break;
case WHOWAS_MAINTAIN:
MaintainWhoWas(ServerInstance->Time());
break;
default:
break;
}
return CMD_SUCCESS;
}
void cmd_whowas::GetStats(Extensible* ext)
{
int whowas_size = 0;
int whowas_bytes = 0;
whowas_users_fifo::iterator iter;
for (iter = whowas_fifo.begin(); iter != whowas_fifo.end(); iter++)
{
whowas_set* n = (whowas_set*)whowas.find(iter->second)->second;
if (n->size())
{
whowas_size += n->size();
whowas_bytes += (sizeof(whowas_set) + ( sizeof(WhoWasGroup) * n->size() ) );
}
}
stats.assign("Whowas(MAPSETS) " +ConvToStr(whowas_size)+" ("+ConvToStr(whowas_bytes)+" bytes)");
ext->Extend("stats", stats.c_str());
}
void cmd_whowas::AddToWhoWas(userrec* user)
{
/* if whowas disabled */
if (ServerInstance->Config->WhoWasGroupSize == 0 || ServerInstance->Config->WhoWasMaxGroups == 0)
{
return;
}
whowas_users::iterator iter = whowas.find(user->nick);
if (iter == whowas.end())
{
whowas_set* n = new whowas_set;
WhoWasGroup *a = new WhoWasGroup(user);
n->push_back(a);
whowas[user->nick] = n;
whowas_fifo.push_back(std::make_pair(ServerInstance->Time(),user->nick));
if ((int)(whowas.size()) > ServerInstance->Config->WhoWasMaxGroups)
{
whowas_users::iterator iter = whowas.find(whowas_fifo[0].second);
if (iter != whowas.end())
{
whowas_set* n = (whowas_set*)iter->second;
if (n->size())
{
while (n->begin() != n->end())
{
WhoWasGroup *a = *(n->begin());
DELETE(a);
n->pop_front();
}
}
DELETE(n);
whowas.erase(iter);
}
whowas_fifo.pop_front();
}
}
else
{
whowas_set* group = (whowas_set*)iter->second;
WhoWasGroup *a = new WhoWasGroup(user);
group->push_back(a);
if ((int)(group->size()) > ServerInstance->Config->WhoWasGroupSize)
{
WhoWasGroup *a = (WhoWasGroup*)*(group->begin());
DELETE(a);
group->pop_front();
}
}
}
/* on rehash, refactor maps according to new conf values */
void cmd_whowas::PruneWhoWas(time_t t)
{
/* config values */
int groupsize = ServerInstance->Config->WhoWasGroupSize;
int maxgroups = ServerInstance->Config->WhoWasMaxGroups;
int maxkeep = ServerInstance->Config->WhoWasMaxKeep;
/* first cut the list to new size (maxgroups) and also prune entries that are timed out. */
whowas_users::iterator iter;
int fifosize;
while ((fifosize = (int)whowas_fifo.size()) > 0)
{
if (fifosize > maxgroups || whowas_fifo[0].first < t - maxkeep)
{
iter = whowas.find(whowas_fifo[0].second);
/* hopefully redundant integrity check, but added while debugging r6216 */
if (iter == whowas.end())
{
/* this should never happen, if it does maps are corrupt */
ServerInstance->Log(DEFAULT, "BUG: Whowas maps got corrupted! (1)");
return;
}
whowas_set* n = (whowas_set*)iter->second;
if (n->size())
{
while (n->begin() != n->end())
{
WhoWasGroup *a = *(n->begin());
DELETE(a);
n->pop_front();
}
}
DELETE(n);
whowas.erase(iter);
whowas_fifo.pop_front();
}
else
break;
}
/* Then cut the whowas sets to new size (groupsize) */
fifosize = (int)whowas_fifo.size();
for (int i = 0; i < fifosize; i++)
{
iter = whowas.find(whowas_fifo[0].second);
/* hopefully redundant integrity check, but added while debugging r6216 */
if (iter == whowas.end())
{
/* this should never happen, if it does maps are corrupt */
ServerInstance->Log(DEFAULT, "BUG: Whowas maps got corrupted! (2)");
return;
}
whowas_set* n = (whowas_set*)iter->second;
if (n->size())
{
int nickcount = n->size();
while (n->begin() != n->end() && nickcount > groupsize)
{
WhoWasGroup *a = *(n->begin());
DELETE(a);
n->pop_front();
nickcount--;
}
}
}
}
/* call maintain once an hour to remove expired nicks */
void cmd_whowas::MaintainWhoWas(time_t t)
{
for (whowas_users::iterator iter = whowas.begin(); iter != whowas.end(); iter++)
{
whowas_set* n = (whowas_set*)iter->second;
if (n->size())
{
while ((n->begin() != n->end()) && ((*n->begin())->signon < t - ServerInstance->Config->WhoWasMaxKeep))
{
WhoWasGroup *a = *(n->begin());
DELETE(a);
n->erase(n->begin());
}
}
}
}
cmd_whowas::~cmd_whowas()
{
if (timer)
{
ServerInstance->Timers->DelTimer(timer);
}
whowas_users::iterator iter;
int fifosize;
while ((fifosize = (int)whowas_fifo.size()) > 0)
{
iter = whowas.find(whowas_fifo[0].second);
/* hopefully redundant integrity check, but added while debugging r6216 */
if (iter == whowas.end())
{
/* this should never happen, if it does maps are corrupt */
ServerInstance->Log(DEFAULT, "BUG: Whowas maps got corrupted! (3)");
return;
}
whowas_set* n = (whowas_set*)iter->second;
if (n->size())
{
while (n->begin() != n->end())
{
WhoWasGroup *a = *(n->begin());
DELETE(a);
n->pop_front();
}
}
DELETE(n);
whowas.erase(iter);
whowas_fifo.pop_front();
}
}
WhoWasGroup::WhoWasGroup(userrec* user) : host(NULL), dhost(NULL), ident(NULL), server(NULL), gecos(NULL), signon(user->signon)
{
this->host = strdup(user->host);
this->dhost = strdup(user->dhost);
this->ident = strdup(user->ident);
this->server = user->server;
this->gecos = strdup(user->fullname);
}
WhoWasGroup::~WhoWasGroup()
{
if (host)
free(host);
if (dhost)
free(dhost);
if (ident)
free(ident);
if (gecos)
free(gecos);
}
/* every hour, run this function which removes all entries older than Config->WhoWasMaxKeep */
void WhoWasMaintainTimer::Tick(time_t t)
{
command_t* whowas_command = ServerInstance->Parser->GetHandler("WHOWAS");
if (whowas_command)
{
std::deque<classbase*> params;
whowas_command->HandleInternal(WHOWAS_MAINTAIN, params);
}
}
\ No newline at end of file +/* +------------------------------------+ + * | 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 "users.h" +#include "commands/cmd_whowas.h" + +WhoWasMaintainTimer * timer; + +extern "C" DllExport command_t* init_command(InspIRCd* Instance) +{ + return new cmd_whowas(Instance); +} + +cmd_whowas::cmd_whowas(InspIRCd* Instance) +: command_t(Instance, "WHOWAS", 0, 1) +{ + syntax = "<nick>{,<nick>}"; + timer = new WhoWasMaintainTimer(Instance, 3600); + Instance->Timers->AddTimer(timer); +} + +CmdResult cmd_whowas::Handle (const char** parameters, int pcnt, userrec* user) +{ + /* if whowas disabled in config */ + if (ServerInstance->Config->WhoWasGroupSize == 0 || ServerInstance->Config->WhoWasMaxGroups == 0) + { + user->WriteServ("421 %s %s :This command has been disabled.",user->nick,command.c_str()); + return CMD_FAILURE; + } + + whowas_users::iterator i = whowas.find(parameters[0]); + + if (i == whowas.end()) + { + user->WriteServ("406 %s %s :There was no such nickname",user->nick,parameters[0]); + user->WriteServ("369 %s %s :End of WHOWAS",user->nick,parameters[0]); + return CMD_FAILURE; + } + else + { + whowas_set* grp = i->second; + if (grp->size()) + { + for (whowas_set::iterator ux = grp->begin(); ux != grp->end(); ux++) + { + WhoWasGroup* u = *ux; + time_t rawtime = u->signon; + tm *timeinfo; + char b[MAXBUF]; + + timeinfo = localtime(&rawtime); + + /* XXX - 'b' could be only 25 chars long and then strlcpy() would terminate it for us too? */ + strlcpy(b,asctime(timeinfo),MAXBUF); + b[24] = 0; + + user->WriteServ("314 %s %s %s %s * :%s",user->nick,parameters[0],u->ident,u->dhost,u->gecos); + + if (IS_OPER(user)) + user->WriteServ("379 %s %s :was connecting from *@%s", user->nick, parameters[0], u->host); + + if (*ServerInstance->Config->HideWhoisServer && !IS_OPER(user)) + user->WriteServ("312 %s %s %s :%s",user->nick,parameters[0], ServerInstance->Config->HideWhoisServer, b); + else + user->WriteServ("312 %s %s %s :%s",user->nick,parameters[0], u->server, b); + } + } + else + { + user->WriteServ("406 %s %s :There was no such nickname",user->nick,parameters[0]); + user->WriteServ("369 %s %s :End of WHOWAS",user->nick,parameters[0]); + return CMD_FAILURE; + } + } + + user->WriteServ("369 %s %s :End of WHOWAS",user->nick,parameters[0]); + return CMD_SUCCESS; +} + +CmdResult cmd_whowas::HandleInternal(const unsigned int id, const std::deque<classbase*> ¶meters) +{ + switch (id) + { + case WHOWAS_ADD: + AddToWhoWas((userrec*)parameters[0]); + break; + + case WHOWAS_STATS: + GetStats((Extensible*)parameters[0]); + break; + + case WHOWAS_PRUNE: + PruneWhoWas(ServerInstance->Time()); + break; + + case WHOWAS_MAINTAIN: + MaintainWhoWas(ServerInstance->Time()); + break; + + default: + break; + } + return CMD_SUCCESS; +} + +void cmd_whowas::GetStats(Extensible* ext) +{ + int whowas_size = 0; + int whowas_bytes = 0; + whowas_users_fifo::iterator iter; + for (iter = whowas_fifo.begin(); iter != whowas_fifo.end(); iter++) + { + whowas_set* n = (whowas_set*)whowas.find(iter->second)->second; + if (n->size()) + { + whowas_size += n->size(); + whowas_bytes += (sizeof(whowas_set) + ( sizeof(WhoWasGroup) * n->size() ) ); + } + } + stats.assign("Whowas(MAPSETS) " +ConvToStr(whowas_size)+" ("+ConvToStr(whowas_bytes)+" bytes)"); + ext->Extend("stats", stats.c_str()); +} + +void cmd_whowas::AddToWhoWas(userrec* user) +{ + /* if whowas disabled */ + if (ServerInstance->Config->WhoWasGroupSize == 0 || ServerInstance->Config->WhoWasMaxGroups == 0) + { + return; + } + + whowas_users::iterator iter = whowas.find(user->nick); + + if (iter == whowas.end()) + { + whowas_set* n = new whowas_set; + WhoWasGroup *a = new WhoWasGroup(user); + n->push_back(a); + whowas[user->nick] = n; + whowas_fifo.push_back(std::make_pair(ServerInstance->Time(),user->nick)); + + if ((int)(whowas.size()) > ServerInstance->Config->WhoWasMaxGroups) + { + whowas_users::iterator iter = whowas.find(whowas_fifo[0].second); + if (iter != whowas.end()) + { + whowas_set* n = (whowas_set*)iter->second; + if (n->size()) + { + while (n->begin() != n->end()) + { + WhoWasGroup *a = *(n->begin()); + DELETE(a); + n->pop_front(); + } + } + DELETE(n); + whowas.erase(iter); + } + whowas_fifo.pop_front(); + } + } + else + { + whowas_set* group = (whowas_set*)iter->second; + WhoWasGroup *a = new WhoWasGroup(user); + group->push_back(a); + + if ((int)(group->size()) > ServerInstance->Config->WhoWasGroupSize) + { + WhoWasGroup *a = (WhoWasGroup*)*(group->begin()); + DELETE(a); + group->pop_front(); + } + } +} + +/* on rehash, refactor maps according to new conf values */ +void cmd_whowas::PruneWhoWas(time_t t) +{ + /* config values */ + int groupsize = ServerInstance->Config->WhoWasGroupSize; + int maxgroups = ServerInstance->Config->WhoWasMaxGroups; + int maxkeep = ServerInstance->Config->WhoWasMaxKeep; + + /* first cut the list to new size (maxgroups) and also prune entries that are timed out. */ + whowas_users::iterator iter; + int fifosize; + while ((fifosize = (int)whowas_fifo.size()) > 0) + { + if (fifosize > maxgroups || whowas_fifo[0].first < t - maxkeep) + { + iter = whowas.find(whowas_fifo[0].second); + /* hopefully redundant integrity check, but added while debugging r6216 */ + if (iter == whowas.end()) + { + /* this should never happen, if it does maps are corrupt */ + ServerInstance->Log(DEFAULT, "BUG: Whowas maps got corrupted! (1)"); + return; + } + whowas_set* n = (whowas_set*)iter->second; + if (n->size()) + { + while (n->begin() != n->end()) + { + WhoWasGroup *a = *(n->begin()); + DELETE(a); + n->pop_front(); + } + } + DELETE(n); + whowas.erase(iter); + whowas_fifo.pop_front(); + } + else + break; + } + + /* Then cut the whowas sets to new size (groupsize) */ + fifosize = (int)whowas_fifo.size(); + for (int i = 0; i < fifosize; i++) + { + iter = whowas.find(whowas_fifo[0].second); + /* hopefully redundant integrity check, but added while debugging r6216 */ + if (iter == whowas.end()) + { + /* this should never happen, if it does maps are corrupt */ + ServerInstance->Log(DEFAULT, "BUG: Whowas maps got corrupted! (2)"); + return; + } + whowas_set* n = (whowas_set*)iter->second; + if (n->size()) + { + int nickcount = n->size(); + while (n->begin() != n->end() && nickcount > groupsize) + { + WhoWasGroup *a = *(n->begin()); + DELETE(a); + n->pop_front(); + nickcount--; + } + } + } +} + +/* call maintain once an hour to remove expired nicks */ +void cmd_whowas::MaintainWhoWas(time_t t) +{ + for (whowas_users::iterator iter = whowas.begin(); iter != whowas.end(); iter++) + { + whowas_set* n = (whowas_set*)iter->second; + if (n->size()) + { + while ((n->begin() != n->end()) && ((*n->begin())->signon < t - ServerInstance->Config->WhoWasMaxKeep)) + { + WhoWasGroup *a = *(n->begin()); + DELETE(a); + n->erase(n->begin()); + } + } + } +} + +cmd_whowas::~cmd_whowas() +{ + if (timer) + { + ServerInstance->Timers->DelTimer(timer); + } + + whowas_users::iterator iter; + int fifosize; + while ((fifosize = (int)whowas_fifo.size()) > 0) + { + iter = whowas.find(whowas_fifo[0].second); + /* hopefully redundant integrity check, but added while debugging r6216 */ + if (iter == whowas.end()) + { + /* this should never happen, if it does maps are corrupt */ + ServerInstance->Log(DEFAULT, "BUG: Whowas maps got corrupted! (3)"); + return; + } + whowas_set* n = (whowas_set*)iter->second; + if (n->size()) + { + while (n->begin() != n->end()) + { + WhoWasGroup *a = *(n->begin()); + DELETE(a); + n->pop_front(); + } + } + DELETE(n); + whowas.erase(iter); + whowas_fifo.pop_front(); + } +} + +WhoWasGroup::WhoWasGroup(userrec* user) : host(NULL), dhost(NULL), ident(NULL), server(NULL), gecos(NULL), signon(user->signon) +{ + this->host = strdup(user->host); + this->dhost = strdup(user->dhost); + this->ident = strdup(user->ident); + this->server = user->server; + this->gecos = strdup(user->fullname); +} + +WhoWasGroup::~WhoWasGroup() +{ + if (host) + free(host); + if (dhost) + free(dhost); + if (ident) + free(ident); + if (gecos) + free(gecos); +} + +/* every hour, run this function which removes all entries older than Config->WhoWasMaxKeep */ +void WhoWasMaintainTimer::Tick(time_t t) +{ + command_t* whowas_command = ServerInstance->Parser->GetHandler("WHOWAS"); + if (whowas_command) + { + std::deque<classbase*> params; + whowas_command->HandleInternal(WHOWAS_MAINTAIN, params); + } +} diff --git a/src/cmd_zline.cpp b/src/cmd_zline.cpp index 43a0fd042..8fea70656 100644 --- a/src/cmd_zline.cpp +++ b/src/cmd_zline.cpp @@ -1 +1,80 @@ -/* +------------------------------------+
* | 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 "users.h"
#include "modules.h"
#include "xline.h"
#include "commands/cmd_zline.h"
extern "C" DllExport command_t* init_command(InspIRCd* Instance)
{
return new cmd_zline(Instance);
}
CmdResult cmd_zline::Handle (const char** parameters, int pcnt, userrec *user)
{
if (pcnt >= 3)
{
if (strchr(parameters[0],'@') || strchr(parameters[0],'!'))
{
user->WriteServ("NOTICE %s :*** You cannot include a username or nickname in a zline, a zline must ban only an IP mask",user->nick);
return CMD_FAILURE;
}
if (ServerInstance->IPMatchesEveryone(parameters[0],user))
return CMD_FAILURE;
long duration = ServerInstance->Duration(parameters[1]);
if (ServerInstance->XLines->add_zline(duration,user->nick,parameters[2],parameters[0]))
{
int to_apply = APPLY_ZLINES;
FOREACH_MOD(I_OnAddZLine,OnAddZLine(duration, user, parameters[2], parameters[0]));
if (!duration)
{
to_apply |= APPLY_PERM_ONLY;
ServerInstance->SNO->WriteToSnoMask('x',"%s added permanent Z-line for %s.",user->nick,parameters[0]);
}
else
{
time_t c_requires_crap = duration + ServerInstance->Time();
ServerInstance->SNO->WriteToSnoMask('x',"%s added timed Z-line for %s, expires on %s",user->nick,parameters[0],
ServerInstance->TimeString(c_requires_crap).c_str());
}
ServerInstance->XLines->apply_lines(to_apply);
}
else
{
user->WriteServ("NOTICE %s :*** Z-Line for %s already exists",user->nick,parameters[0]);
}
}
else
{
if (ServerInstance->XLines->del_zline(parameters[0]))
{
FOREACH_MOD(I_OnDelZLine,OnDelZLine(user, parameters[0]));
ServerInstance->SNO->WriteToSnoMask('x',"%s Removed Z-line on %s.",user->nick,parameters[0]);
}
else
{
user->WriteServ("NOTICE %s :*** Z-Line %s not found in list, try /stats Z.",user->nick,parameters[0]);
return CMD_FAILURE;
}
}
return CMD_SUCCESS;
}
\ No newline at end of file +/* +------------------------------------+ + * | 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 "users.h" +#include "modules.h" +#include "xline.h" +#include "commands/cmd_zline.h" + + + +extern "C" DllExport command_t* init_command(InspIRCd* Instance) +{ + return new cmd_zline(Instance); +} + +CmdResult cmd_zline::Handle (const char** parameters, int pcnt, userrec *user) +{ + if (pcnt >= 3) + { + if (strchr(parameters[0],'@') || strchr(parameters[0],'!')) + { + user->WriteServ("NOTICE %s :*** You cannot include a username or nickname in a zline, a zline must ban only an IP mask",user->nick); + return CMD_FAILURE; + } + + if (ServerInstance->IPMatchesEveryone(parameters[0],user)) + return CMD_FAILURE; + + long duration = ServerInstance->Duration(parameters[1]); + if (ServerInstance->XLines->add_zline(duration,user->nick,parameters[2],parameters[0])) + { + int to_apply = APPLY_ZLINES; + + FOREACH_MOD(I_OnAddZLine,OnAddZLine(duration, user, parameters[2], parameters[0])); + if (!duration) + { + to_apply |= APPLY_PERM_ONLY; + ServerInstance->SNO->WriteToSnoMask('x',"%s added permanent Z-line for %s.",user->nick,parameters[0]); + } + else + { + time_t c_requires_crap = duration + ServerInstance->Time(); + ServerInstance->SNO->WriteToSnoMask('x',"%s added timed Z-line for %s, expires on %s",user->nick,parameters[0], + ServerInstance->TimeString(c_requires_crap).c_str()); + } + ServerInstance->XLines->apply_lines(to_apply); + } + else + { + user->WriteServ("NOTICE %s :*** Z-Line for %s already exists",user->nick,parameters[0]); + } + } + else + { + if (ServerInstance->XLines->del_zline(parameters[0])) + { + FOREACH_MOD(I_OnDelZLine,OnDelZLine(user, parameters[0])); + ServerInstance->SNO->WriteToSnoMask('x',"%s Removed Z-line on %s.",user->nick,parameters[0]); + } + else + { + user->WriteServ("NOTICE %s :*** Z-Line %s not found in list, try /stats Z.",user->nick,parameters[0]); + return CMD_FAILURE; + } + } + + return CMD_SUCCESS; +} diff --git a/src/command_parse.cpp b/src/command_parse.cpp index bf2e61ce9..52c58ef99 100644 --- a/src/command_parse.cpp +++ b/src/command_parse.cpp @@ -1 +1,563 @@ -/* +------------------------------------+
* | 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 <algorithm>
#include "users.h"
#include "modules.h"
#include "wildcard.h"
#include "xline.h"
#include "socketengine.h"
#include "socket.h"
#include "command_parse.h"
/* Directory Searching for Unix-Only */
#ifndef WIN32
#include <dirent.h>
#include <dlfcn.h>
#endif
bool InspIRCd::ULine(const char* server)
{
if (!server)
return false;
if (!*server)
return true;
return (Config->ulines.find(server) != Config->ulines.end());
}
bool InspIRCd::SilentULine(const char* server)
{
std::map<irc::string,bool>::iterator n = Config->ulines.find(server);
if (n != Config->ulines.end())
return n->second;
else return false;
}
int InspIRCd::OperPassCompare(const char* data,const char* input, int tagnumber)
{
int MOD_RESULT = 0;
FOREACH_RESULT_I(this,I_OnOperCompare,OnOperCompare(data, input, tagnumber))
if (MOD_RESULT == 1)
return 0;
if (MOD_RESULT == -1)
return 1;
return strcmp(data,input);
}
std::string InspIRCd::TimeString(time_t curtime)
{
return std::string(ctime(&curtime),24);
}
/** Refactored by Brain, Jun 2007. Much faster with some clever O(1) array
* lookups and pointer maths.
*/
long InspIRCd::Duration(const std::string &str)
{
unsigned char multiplier = 0;
long total = 0;
long times = 1;
long subtotal = 0;
/* Iterate each item in the string, looking for number or multiplier */
for (std::string::const_reverse_iterator i = str.rbegin(); i != str.rend(); ++i)
{
/* Found a number, queue it onto the current number */
if ((*i >= '0') && (*i <= '9'))
{
subtotal = subtotal + ((*i - '0') * times);
times = times * 10;
}
else
{
/* Found something thats not a number, find out how much
* it multiplies the built up number by, multiply the total
* and reset the built up number.
*/
if (subtotal)
total += subtotal * duration_multi[multiplier];
/* Next subtotal please */
subtotal = 0;
multiplier = *i;
times = 1;
}
}
if (multiplier)
{
total += subtotal * duration_multi[multiplier];
subtotal = 0;
}
/* Any trailing values built up are treated as raw seconds */
return total + subtotal;
}
/* LoopCall is used to call a command classes handler repeatedly based on the contents of a comma seperated list.
* There are two overriden versions of this method, one of which takes two potential lists and the other takes one.
* We need a version which takes two potential lists for JOIN, because a JOIN may contain two lists of items at once,
* the channel names and their keys as follows:
* JOIN #chan1,#chan2,#chan3 key1,,key3
* Therefore, we need to deal with both lists concurrently. The first instance of this method does that by creating
* two instances of irc::commasepstream and reading them both together until the first runs out of tokens.
* The second version is much simpler and just has the one stream to read, and is used in NAMES, WHOIS, PRIVMSG etc.
* Both will only parse until they reach ServerInstance->Config->MaxTargets number of targets, to stop abuse via spam.
*/
int CommandParser::LoopCall(userrec* user, command_t* CommandObj, const char** parameters, int pcnt, unsigned int splithere, unsigned int extra)
{
/* First check if we have more than one item in the list, if we don't we return zero here and the handler
* which called us just carries on as it was.
*/
if (!strchr(parameters[splithere],','))
return 0;
/** Some lame ircds will weed out dupes using some shitty O(n^2) algorithm.
* By using std::map (thanks for the idea w00t) we can cut this down a ton.
* ...VOOODOOOO!
*/
std::map<irc::string, bool> dupes;
/* Create two lists, one for channel names, one for keys
*/
irc::commasepstream items1(parameters[splithere]);
irc::commasepstream items2(parameters[extra]);
std::string item("*");
unsigned int max = 0;
/* Attempt to iterate these lists and call the command objech
* which called us, for every parameter pair until there are
* no more left to parse.
*/
while (((item = items1.GetToken()) != "") && (max++ < ServerInstance->Config->MaxTargets))
{
if (dupes.find(item.c_str()) == dupes.end())
{
const char* new_parameters[127];
for (int t = 0; (t < pcnt) && (t < 127); t++)
new_parameters[t] = parameters[t];
std::string extrastuff = items2.GetToken();
new_parameters[splithere] = item.c_str();
new_parameters[extra] = extrastuff.c_str();
CommandObj->Handle(new_parameters,pcnt,user);
dupes[item.c_str()] = true;
}
}
return 1;
}
int CommandParser::LoopCall(userrec* user, command_t* CommandObj, const char** parameters, int pcnt, unsigned int splithere)
{
/* First check if we have more than one item in the list, if we don't we return zero here and the handler
* which called us just carries on as it was.
*/
if (!strchr(parameters[splithere],','))
return 0;
std::map<irc::string, bool> dupes;
/* Only one commasepstream here */
irc::commasepstream items1(parameters[splithere]);
std::string item("*");
unsigned int max = 0;
/* Parse the commasepstream until there are no tokens remaining.
* Each token we parse out, call the command handler that called us
* with it
*/
while (((item = items1.GetToken()) != "") && (max++ < ServerInstance->Config->MaxTargets))
{
if (dupes.find(item.c_str()) == dupes.end())
{
const char* new_parameters[127];
for (int t = 0; (t < pcnt) && (t < 127); t++)
new_parameters[t] = parameters[t];
new_parameters[splithere] = item.c_str();
parameters[splithere] = item.c_str();
/* Execute the command handler over and over. If someone pulls our user
* record out from under us (e.g. if we /kill a comma sep list, and we're
* in that list ourselves) abort if we're gone.
*/
CommandObj->Handle(new_parameters,pcnt,user);
dupes[item.c_str()] = true;
}
}
/* By returning 1 we tell our caller that nothing is to be done,
* as all the previous calls handled the data. This makes the parent
* return without doing any processing.
*/
return 1;
}
bool CommandParser::IsValidCommand(const std::string &commandname, int pcnt, userrec * user)
{
command_table::iterator n = cmdlist.find(commandname);
if (n != cmdlist.end())
{
if ((pcnt>=n->second->min_params) && (n->second->source != "<core>"))
{
if ((!n->second->flags_needed) || (user->modes[n->second->flags_needed-65]))
{
if (n->second->flags_needed)
{
return ((user->HasPermission(commandname)) || (ServerInstance->ULine(user->server)));
}
return true;
}
}
}
return false;
}
command_t* CommandParser::GetHandler(const std::string &commandname)
{
command_table::iterator n = cmdlist.find(commandname);
if (n != cmdlist.end())
return n->second;
return NULL;
}
// calls a handler function for a command
CmdResult CommandParser::CallHandler(const std::string &commandname,const char** parameters, int pcnt, userrec *user)
{
command_table::iterator n = cmdlist.find(commandname);
if (n != cmdlist.end())
{
if (pcnt >= n->second->min_params)
{
if ((!n->second->flags_needed) || (user->modes[n->second->flags_needed-65]))
{
if (n->second->flags_needed)
{
if ((user->HasPermission(commandname)) || (!IS_LOCAL(user)))
{
return n->second->Handle(parameters,pcnt,user);
}
}
else
{
return n->second->Handle(parameters,pcnt,user);
}
}
}
}
return CMD_INVALID;
}
void CommandParser::ProcessCommand(userrec *user, std::string &cmd)
{
const char *command_p[127];
int items = 0;
irc::tokenstream tokens(cmd);
std::string command;
tokens.GetToken(command);
/* A client sent a nick prefix on their command (ick)
* rhapsody and some braindead bouncers do this --
* the rfc says they shouldnt but also says the ircd should
* discard it if they do.
*/
if (*command.c_str() == ':')
tokens.GetToken(command);
while (tokens.GetToken(para[items]) && (items < 127))
{
command_p[items] = para[items].c_str();
items++;
}
std::transform(command.begin(), command.end(), command.begin(), ::toupper);
int MOD_RESULT = 0;
FOREACH_RESULT(I_OnPreCommand,OnPreCommand(command,command_p,items,user,false,cmd));
if (MOD_RESULT == 1) {
return;
}
command_table::iterator cm = cmdlist.find(command);
if (cm != cmdlist.end())
{
if (user)
{
/* activity resets the ping pending timer */
user->nping = ServerInstance->Time() + user->pingmax;
if (cm->second->flags_needed)
{
if (!user->IsModeSet(cm->second->flags_needed))
{
user->WriteServ("481 %s :Permission Denied - You do not have the required operator privileges",user->nick);
return;
}
if (!user->HasPermission(command))
{
user->WriteServ("481 %s :Permission Denied - Oper type %s does not have access to command %s",user->nick,user->oper,command.c_str());
return;
}
}
if ((user->registered == REG_ALL) && (!IS_OPER(user)) && (cm->second->IsDisabled()))
{
/* command is disabled! */
user->WriteServ("421 %s %s :This command has been disabled.",user->nick,command.c_str());
return;
}
if (items < cm->second->min_params)
{
user->WriteServ("461 %s %s :Not enough parameters.", user->nick, command.c_str());
/* If syntax is given, display this as the 461 reply */
if ((ServerInstance->Config->SyntaxHints) && (user->registered == REG_ALL) && (cm->second->syntax.length()))
user->WriteServ("304 %s :SYNTAX %s %s", user->nick, cm->second->command.c_str(), cm->second->syntax.c_str());
return;
}
if ((user->registered == REG_ALL) || (cm->second->WorksBeforeReg()))
{
/* ikky /stats counters */
cm->second->use_count++;
cm->second->total_bytes += cmd.length();
int MOD_RESULT = 0;
FOREACH_RESULT(I_OnPreCommand,OnPreCommand(command,command_p,items,user,true,cmd));
if (MOD_RESULT == 1)
return;
/*
* WARNING: nothing may come after the
* command handler call, as the handler
* may free the user structure!
*/
CmdResult result = cm->second->Handle(command_p,items,user);
FOREACH_MOD(I_OnPostCommand,OnPostCommand(command, command_p, items, user, result,cmd));
return;
}
else
{
user->WriteServ("451 %s :You have not registered",command.c_str());
return;
}
}
}
else if (user)
{
ServerInstance->stats->statsUnknown++;
user->WriteServ("421 %s %s :Unknown command",user->nick,command.c_str());
}
}
bool CommandParser::RemoveCommands(const char* source)
{
command_table::iterator i,safei;
for (i = cmdlist.begin(); i != cmdlist.end(); i++)
{
safei = i;
safei++;
if (safei != cmdlist.end())
{
RemoveCommand(safei, source);
}
}
safei = cmdlist.begin();
if (safei != cmdlist.end())
{
RemoveCommand(safei, source);
}
return true;
}
void CommandParser::RemoveCommand(command_table::iterator safei, const char* source)
{
command_t* x = safei->second;
if (x->source == std::string(source))
{
cmdlist.erase(safei);
delete x;
}
}
void CommandParser::ProcessBuffer(std::string &buffer,userrec *user)
{
std::string::size_type a;
if (!user)
return;
while ((a = buffer.rfind("\n")) != std::string::npos)
buffer.erase(a);
while ((a = buffer.rfind("\r")) != std::string::npos)
buffer.erase(a);
if (buffer.length())
{
if (!user->muted)
{
ServerInstance->Log(DEBUG,"C[%d] -> :%s %s",user->GetFd(), user->nick, buffer.c_str());
this->ProcessCommand(user,buffer);
}
}
}
bool CommandParser::CreateCommand(command_t *f, void* so_handle)
{
if (so_handle)
{
if (RFCCommands.find(f->command) == RFCCommands.end())
RFCCommands[f->command] = so_handle;
else
{
ServerInstance->Log(DEFAULT,"ERK! Somehow, we loaded a cmd_*.so file twice! Only the first instance is being recorded.");
return false;
}
}
/* create the command and push it onto the table */
if (cmdlist.find(f->command) == cmdlist.end())
{
cmdlist[f->command] = f;
return true;
}
else return false;
}
CommandParser::CommandParser(InspIRCd* Instance) : ServerInstance(Instance)
{
para.resize(128);
this->SetupCommandTable();
}
bool CommandParser::FindSym(void** v, void* h)
{
*v = dlsym(h, "init_command");
const char* err = dlerror();
if (err && !(*v))
{
ServerInstance->Log(SPARSE, "Error loading core command: %s\n", err);
return false;
}
return true;
}
bool CommandParser::ReloadCommand(const char* cmd)
{
char filename[MAXBUF];
char commandname[MAXBUF];
int y = 0;
for (const char* x = cmd; *x; x++, y++)
commandname[y] = toupper(*x);
commandname[y] = 0;
SharedObjectList::iterator command = RFCCommands.find(commandname);
if (command != RFCCommands.end())
{
command_t* cmdptr = cmdlist.find(commandname)->second;
cmdlist.erase(cmdlist.find(commandname));
for (char* x = commandname; *x; x++)
*x = tolower(*x);
delete cmdptr;
dlclose(command->second);
RFCCommands.erase(command);
snprintf(filename, MAXBUF, "cmd_%s.so", commandname);
this->LoadCommand(filename);
return true;
}
return false;
}
CmdResult cmd_reload::Handle(const char** parameters, int pcnt, userrec *user)
{
user->WriteServ("NOTICE %s :*** Reloading command '%s'",user->nick, parameters[0]);
if (ServerInstance->Parser->ReloadCommand(parameters[0]))
{
user->WriteServ("NOTICE %s :*** Successfully reloaded command '%s'", user->nick, parameters[0]);
ServerInstance->WriteOpers("*** RELOAD: %s reloaded the '%s' command.", user->nick, parameters[0]);
return CMD_SUCCESS;
}
else
{
user->WriteServ("NOTICE %s :*** Could not reload command '%s'", user->nick, parameters[0]);
return CMD_FAILURE;
}
}
void CommandParser::LoadCommand(const char* name)
{
char filename[MAXBUF];
void* h;
command_t* (*cmd_factory_func)(InspIRCd*);
snprintf(filename, MAXBUF, "%s/%s", LIBRARYDIR, name);
h = dlopen(filename, RTLD_NOW | RTLD_GLOBAL);
if (!h)
{
ServerInstance->Log(SPARSE, "Error loading core command: %s", dlerror());
return;
}
if (this->FindSym((void **)&cmd_factory_func, h))
{
command_t* newcommand = cmd_factory_func(ServerInstance);
this->CreateCommand(newcommand, h);
}
}
void CommandParser::SetupCommandTable()
{
RFCCommands.clear();
printf("\nLoading core commands");
fflush(stdout);
DIR* library = opendir(LIBRARYDIR);
if (library)
{
dirent* entry = NULL;
while ((entry = readdir(library)))
{
if (match(entry->d_name, "cmd_*.so"))
{
printf(".");
fflush(stdout);
this->LoadCommand(entry->d_name);
}
}
closedir(library);
printf("\n");
}
this->CreateCommand(new cmd_reload(ServerInstance));
}
\ No newline at end of file +/* +------------------------------------+ + * | 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 <algorithm> +#include "users.h" +#include "modules.h" +#include "wildcard.h" +#include "xline.h" +#include "socketengine.h" +#include "socket.h" +#include "command_parse.h" + +/* Directory Searching for Unix-Only */ +#ifndef WIN32 +#include <dirent.h> +#include <dlfcn.h> +#endif + +bool InspIRCd::ULine(const char* server) +{ + if (!server) + return false; + if (!*server) + return true; + + return (Config->ulines.find(server) != Config->ulines.end()); +} + +bool InspIRCd::SilentULine(const char* server) +{ + std::map<irc::string,bool>::iterator n = Config->ulines.find(server); + if (n != Config->ulines.end()) + return n->second; + else return false; +} + +int InspIRCd::OperPassCompare(const char* data,const char* input, int tagnumber) +{ + int MOD_RESULT = 0; + FOREACH_RESULT_I(this,I_OnOperCompare,OnOperCompare(data, input, tagnumber)) + if (MOD_RESULT == 1) + return 0; + if (MOD_RESULT == -1) + return 1; + return strcmp(data,input); +} + +std::string InspIRCd::TimeString(time_t curtime) +{ + return std::string(ctime(&curtime),24); +} + +/** Refactored by Brain, Jun 2007. Much faster with some clever O(1) array + * lookups and pointer maths. + */ +long InspIRCd::Duration(const std::string &str) +{ + unsigned char multiplier = 0; + long total = 0; + long times = 1; + long subtotal = 0; + + /* Iterate each item in the string, looking for number or multiplier */ + for (std::string::const_reverse_iterator i = str.rbegin(); i != str.rend(); ++i) + { + /* Found a number, queue it onto the current number */ + if ((*i >= '0') && (*i <= '9')) + { + subtotal = subtotal + ((*i - '0') * times); + times = times * 10; + } + else + { + /* Found something thats not a number, find out how much + * it multiplies the built up number by, multiply the total + * and reset the built up number. + */ + if (subtotal) + total += subtotal * duration_multi[multiplier]; + + /* Next subtotal please */ + subtotal = 0; + multiplier = *i; + times = 1; + } + } + if (multiplier) + { + total += subtotal * duration_multi[multiplier]; + subtotal = 0; + } + /* Any trailing values built up are treated as raw seconds */ + return total + subtotal; +} + +/* LoopCall is used to call a command classes handler repeatedly based on the contents of a comma seperated list. + * There are two overriden versions of this method, one of which takes two potential lists and the other takes one. + * We need a version which takes two potential lists for JOIN, because a JOIN may contain two lists of items at once, + * the channel names and their keys as follows: + * JOIN #chan1,#chan2,#chan3 key1,,key3 + * Therefore, we need to deal with both lists concurrently. The first instance of this method does that by creating + * two instances of irc::commasepstream and reading them both together until the first runs out of tokens. + * The second version is much simpler and just has the one stream to read, and is used in NAMES, WHOIS, PRIVMSG etc. + * Both will only parse until they reach ServerInstance->Config->MaxTargets number of targets, to stop abuse via spam. + */ +int CommandParser::LoopCall(userrec* user, command_t* CommandObj, const char** parameters, int pcnt, unsigned int splithere, unsigned int extra) +{ + /* First check if we have more than one item in the list, if we don't we return zero here and the handler + * which called us just carries on as it was. + */ + if (!strchr(parameters[splithere],',')) + return 0; + + /** Some lame ircds will weed out dupes using some shitty O(n^2) algorithm. + * By using std::map (thanks for the idea w00t) we can cut this down a ton. + * ...VOOODOOOO! + */ + std::map<irc::string, bool> dupes; + + /* Create two lists, one for channel names, one for keys + */ + irc::commasepstream items1(parameters[splithere]); + irc::commasepstream items2(parameters[extra]); + std::string item("*"); + unsigned int max = 0; + + /* Attempt to iterate these lists and call the command objech + * which called us, for every parameter pair until there are + * no more left to parse. + */ + while (((item = items1.GetToken()) != "") && (max++ < ServerInstance->Config->MaxTargets)) + { + if (dupes.find(item.c_str()) == dupes.end()) + { + const char* new_parameters[127]; + + for (int t = 0; (t < pcnt) && (t < 127); t++) + new_parameters[t] = parameters[t]; + + std::string extrastuff = items2.GetToken(); + + new_parameters[splithere] = item.c_str(); + new_parameters[extra] = extrastuff.c_str(); + + CommandObj->Handle(new_parameters,pcnt,user); + + dupes[item.c_str()] = true; + } + } + return 1; +} + +int CommandParser::LoopCall(userrec* user, command_t* CommandObj, const char** parameters, int pcnt, unsigned int splithere) +{ + /* First check if we have more than one item in the list, if we don't we return zero here and the handler + * which called us just carries on as it was. + */ + if (!strchr(parameters[splithere],',')) + return 0; + + std::map<irc::string, bool> dupes; + + /* Only one commasepstream here */ + irc::commasepstream items1(parameters[splithere]); + std::string item("*"); + unsigned int max = 0; + + /* Parse the commasepstream until there are no tokens remaining. + * Each token we parse out, call the command handler that called us + * with it + */ + while (((item = items1.GetToken()) != "") && (max++ < ServerInstance->Config->MaxTargets)) + { + if (dupes.find(item.c_str()) == dupes.end()) + { + const char* new_parameters[127]; + + for (int t = 0; (t < pcnt) && (t < 127); t++) + new_parameters[t] = parameters[t]; + + new_parameters[splithere] = item.c_str(); + + parameters[splithere] = item.c_str(); + + /* Execute the command handler over and over. If someone pulls our user + * record out from under us (e.g. if we /kill a comma sep list, and we're + * in that list ourselves) abort if we're gone. + */ + CommandObj->Handle(new_parameters,pcnt,user); + + dupes[item.c_str()] = true; + } + } + /* By returning 1 we tell our caller that nothing is to be done, + * as all the previous calls handled the data. This makes the parent + * return without doing any processing. + */ + return 1; +} + +bool CommandParser::IsValidCommand(const std::string &commandname, int pcnt, userrec * user) +{ + command_table::iterator n = cmdlist.find(commandname); + + if (n != cmdlist.end()) + { + if ((pcnt>=n->second->min_params) && (n->second->source != "<core>")) + { + if ((!n->second->flags_needed) || (user->modes[n->second->flags_needed-65])) + { + if (n->second->flags_needed) + { + return ((user->HasPermission(commandname)) || (ServerInstance->ULine(user->server))); + } + return true; + } + } + } + return false; +} + +command_t* CommandParser::GetHandler(const std::string &commandname) +{ + command_table::iterator n = cmdlist.find(commandname); + if (n != cmdlist.end()) + return n->second; + + return NULL; +} + +// calls a handler function for a command + +CmdResult CommandParser::CallHandler(const std::string &commandname,const char** parameters, int pcnt, userrec *user) +{ + command_table::iterator n = cmdlist.find(commandname); + + if (n != cmdlist.end()) + { + if (pcnt >= n->second->min_params) + { + if ((!n->second->flags_needed) || (user->modes[n->second->flags_needed-65])) + { + if (n->second->flags_needed) + { + if ((user->HasPermission(commandname)) || (!IS_LOCAL(user))) + { + return n->second->Handle(parameters,pcnt,user); + } + } + else + { + return n->second->Handle(parameters,pcnt,user); + } + } + } + } + return CMD_INVALID; +} + +void CommandParser::ProcessCommand(userrec *user, std::string &cmd) +{ + const char *command_p[127]; + int items = 0; + irc::tokenstream tokens(cmd); + std::string command; + tokens.GetToken(command); + + /* A client sent a nick prefix on their command (ick) + * rhapsody and some braindead bouncers do this -- + * the rfc says they shouldnt but also says the ircd should + * discard it if they do. + */ + if (*command.c_str() == ':') + tokens.GetToken(command); + + while (tokens.GetToken(para[items]) && (items < 127)) + { + command_p[items] = para[items].c_str(); + items++; + } + + std::transform(command.begin(), command.end(), command.begin(), ::toupper); + + int MOD_RESULT = 0; + FOREACH_RESULT(I_OnPreCommand,OnPreCommand(command,command_p,items,user,false,cmd)); + if (MOD_RESULT == 1) { + return; + } + + command_table::iterator cm = cmdlist.find(command); + + if (cm != cmdlist.end()) + { + if (user) + { + /* activity resets the ping pending timer */ + user->nping = ServerInstance->Time() + user->pingmax; + if (cm->second->flags_needed) + { + if (!user->IsModeSet(cm->second->flags_needed)) + { + user->WriteServ("481 %s :Permission Denied - You do not have the required operator privileges",user->nick); + return; + } + if (!user->HasPermission(command)) + { + user->WriteServ("481 %s :Permission Denied - Oper type %s does not have access to command %s",user->nick,user->oper,command.c_str()); + return; + } + } + if ((user->registered == REG_ALL) && (!IS_OPER(user)) && (cm->second->IsDisabled())) + { + /* command is disabled! */ + user->WriteServ("421 %s %s :This command has been disabled.",user->nick,command.c_str()); + return; + } + if (items < cm->second->min_params) + { + user->WriteServ("461 %s %s :Not enough parameters.", user->nick, command.c_str()); + /* If syntax is given, display this as the 461 reply */ + if ((ServerInstance->Config->SyntaxHints) && (user->registered == REG_ALL) && (cm->second->syntax.length())) + user->WriteServ("304 %s :SYNTAX %s %s", user->nick, cm->second->command.c_str(), cm->second->syntax.c_str()); + return; + } + if ((user->registered == REG_ALL) || (cm->second->WorksBeforeReg())) + { + /* ikky /stats counters */ + cm->second->use_count++; + cm->second->total_bytes += cmd.length(); + + int MOD_RESULT = 0; + FOREACH_RESULT(I_OnPreCommand,OnPreCommand(command,command_p,items,user,true,cmd)); + if (MOD_RESULT == 1) + return; + + /* + * WARNING: nothing may come after the + * command handler call, as the handler + * may free the user structure! + */ + CmdResult result = cm->second->Handle(command_p,items,user); + + FOREACH_MOD(I_OnPostCommand,OnPostCommand(command, command_p, items, user, result,cmd)); + return; + } + else + { + user->WriteServ("451 %s :You have not registered",command.c_str()); + return; + } + } + } + else if (user) + { + ServerInstance->stats->statsUnknown++; + user->WriteServ("421 %s %s :Unknown command",user->nick,command.c_str()); + } +} + +bool CommandParser::RemoveCommands(const char* source) +{ + command_table::iterator i,safei; + for (i = cmdlist.begin(); i != cmdlist.end(); i++) + { + safei = i; + safei++; + if (safei != cmdlist.end()) + { + RemoveCommand(safei, source); + } + } + safei = cmdlist.begin(); + if (safei != cmdlist.end()) + { + RemoveCommand(safei, source); + } + return true; +} + +void CommandParser::RemoveCommand(command_table::iterator safei, const char* source) +{ + command_t* x = safei->second; + if (x->source == std::string(source)) + { + cmdlist.erase(safei); + delete x; + } +} + +void CommandParser::ProcessBuffer(std::string &buffer,userrec *user) +{ + std::string::size_type a; + + if (!user) + return; + + while ((a = buffer.rfind("\n")) != std::string::npos) + buffer.erase(a); + while ((a = buffer.rfind("\r")) != std::string::npos) + buffer.erase(a); + + if (buffer.length()) + { + if (!user->muted) + { + ServerInstance->Log(DEBUG,"C[%d] -> :%s %s",user->GetFd(), user->nick, buffer.c_str()); + this->ProcessCommand(user,buffer); + } + } +} + +bool CommandParser::CreateCommand(command_t *f, void* so_handle) +{ + if (so_handle) + { + if (RFCCommands.find(f->command) == RFCCommands.end()) + RFCCommands[f->command] = so_handle; + else + { + ServerInstance->Log(DEFAULT,"ERK! Somehow, we loaded a cmd_*.so file twice! Only the first instance is being recorded."); + return false; + } + } + + /* create the command and push it onto the table */ + if (cmdlist.find(f->command) == cmdlist.end()) + { + cmdlist[f->command] = f; + return true; + } + else return false; +} + +CommandParser::CommandParser(InspIRCd* Instance) : ServerInstance(Instance) +{ + para.resize(128); + this->SetupCommandTable(); +} + +bool CommandParser::FindSym(void** v, void* h) +{ + *v = dlsym(h, "init_command"); + const char* err = dlerror(); + if (err && !(*v)) + { + ServerInstance->Log(SPARSE, "Error loading core command: %s\n", err); + return false; + } + return true; +} + +bool CommandParser::ReloadCommand(const char* cmd) +{ + char filename[MAXBUF]; + char commandname[MAXBUF]; + int y = 0; + + for (const char* x = cmd; *x; x++, y++) + commandname[y] = toupper(*x); + + commandname[y] = 0; + + SharedObjectList::iterator command = RFCCommands.find(commandname); + + if (command != RFCCommands.end()) + { + command_t* cmdptr = cmdlist.find(commandname)->second; + cmdlist.erase(cmdlist.find(commandname)); + + for (char* x = commandname; *x; x++) + *x = tolower(*x); + + + delete cmdptr; + dlclose(command->second); + RFCCommands.erase(command); + + snprintf(filename, MAXBUF, "cmd_%s.so", commandname); + this->LoadCommand(filename); + + return true; + } + + return false; +} + +CmdResult cmd_reload::Handle(const char** parameters, int pcnt, userrec *user) +{ + user->WriteServ("NOTICE %s :*** Reloading command '%s'",user->nick, parameters[0]); + if (ServerInstance->Parser->ReloadCommand(parameters[0])) + { + user->WriteServ("NOTICE %s :*** Successfully reloaded command '%s'", user->nick, parameters[0]); + ServerInstance->WriteOpers("*** RELOAD: %s reloaded the '%s' command.", user->nick, parameters[0]); + return CMD_SUCCESS; + } + else + { + user->WriteServ("NOTICE %s :*** Could not reload command '%s'", user->nick, parameters[0]); + return CMD_FAILURE; + } +} + +void CommandParser::LoadCommand(const char* name) +{ + char filename[MAXBUF]; + void* h; + command_t* (*cmd_factory_func)(InspIRCd*); + + snprintf(filename, MAXBUF, "%s/%s", LIBRARYDIR, name); + h = dlopen(filename, RTLD_NOW | RTLD_GLOBAL); + + if (!h) + { + ServerInstance->Log(SPARSE, "Error loading core command: %s", dlerror()); + return; + } + + if (this->FindSym((void **)&cmd_factory_func, h)) + { + command_t* newcommand = cmd_factory_func(ServerInstance); + this->CreateCommand(newcommand, h); + } +} + +void CommandParser::SetupCommandTable() +{ + RFCCommands.clear(); + + printf("\nLoading core commands"); + fflush(stdout); + + DIR* library = opendir(LIBRARYDIR); + if (library) + { + dirent* entry = NULL; + while ((entry = readdir(library))) + { + if (match(entry->d_name, "cmd_*.so")) + { + printf("."); + fflush(stdout); + this->LoadCommand(entry->d_name); + } + } + closedir(library); + printf("\n"); + } + + this->CreateCommand(new cmd_reload(ServerInstance)); +} + diff --git a/src/commands.cpp b/src/commands.cpp index 469b4ed74..65b11e5a0 100644 --- a/src/commands.cpp +++ b/src/commands.cpp @@ -1 +1,111 @@ -/* +------------------------------------+
* | 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 "users.h"
#include "modules.h"
#include "wildcard.h"
#include "xline.h"
#include "command_parse.h"
/* All other ircds when doing this check usually just look for a string of *@* or *. We're smarter than that, though. */
bool InspIRCd::HostMatchesEveryone(const std::string &mask, userrec* user)
{
char itrigger[MAXBUF];
long matches = 0;
if (!Config->ConfValue(Config->config_data, "insane","trigger", 0, itrigger, MAXBUF))
strlcpy(itrigger,"95.5",MAXBUF);
if (Config->ConfValueBool(Config->config_data, "insane","hostmasks", 0))
return false;
for (user_hash::iterator u = clientlist->begin(); u != clientlist->end(); u++)
{
if ((match(u->second->MakeHost(),mask.c_str(),true)) || (match(u->second->MakeHostIP(),mask.c_str(),true)))
{
matches++;
}
}
if (!matches)
return false;
float percent = ((float)matches / (float)clientlist->size()) * 100;
if (percent > (float)atof(itrigger))
{
WriteOpers("*** \2WARNING\2: %s tried to set a G/K/E line mask of %s, which covers %.2f%% of the network!",user->nick,mask.c_str(),percent);
return true;
}
return false;
}
bool InspIRCd::IPMatchesEveryone(const std::string &ip, userrec* user)
{
char itrigger[MAXBUF];
long matches = 0;
if (!Config->ConfValue(Config->config_data, "insane","trigger",0,itrigger,MAXBUF))
strlcpy(itrigger,"95.5",MAXBUF);
if (Config->ConfValueBool(Config->config_data, "insane","ipmasks",0))
return false;
for (user_hash::iterator u = clientlist->begin(); u != clientlist->end(); u++)
{
if (match(u->second->GetIPString(),ip.c_str(),true))
matches++;
}
if (!matches)
return false;
float percent = ((float)matches / (float)clientlist->size()) * 100;
if (percent > (float)atof(itrigger))
{
WriteOpers("*** \2WARNING\2: %s tried to set a Z line mask of %s, which covers %.2f%% of the network!",user->nick,ip.c_str(),percent);
return true;
}
return false;
}
bool InspIRCd::NickMatchesEveryone(const std::string &nick, userrec* user)
{
char itrigger[MAXBUF];
long matches = 0;
if (!Config->ConfValue(Config->config_data, "insane","trigger",0,itrigger,MAXBUF))
strlcpy(itrigger,"95.5",MAXBUF);
if (Config->ConfValueBool(Config->config_data, "insane","nickmasks",0))
return false;
for (user_hash::iterator u = clientlist->begin(); u != clientlist->end(); u++)
{
if (match(u->second->nick,nick.c_str()))
matches++;
}
if (!matches)
return false;
float percent = ((float)matches / (float)clientlist->size()) * 100;
if (percent > (float)atof(itrigger))
{
WriteOpers("*** \2WARNING\2: %s tried to set a Q line mask of %s, which covers %.2f%% of the network!",user->nick,nick.c_str(),percent);
return true;
}
return false;
}
\ No newline at end of file +/* +------------------------------------+ + * | 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 "users.h" +#include "modules.h" +#include "wildcard.h" +#include "xline.h" +#include "command_parse.h" + +/* All other ircds when doing this check usually just look for a string of *@* or *. We're smarter than that, though. */ + +bool InspIRCd::HostMatchesEveryone(const std::string &mask, userrec* user) +{ + char itrigger[MAXBUF]; + long matches = 0; + + if (!Config->ConfValue(Config->config_data, "insane","trigger", 0, itrigger, MAXBUF)) + strlcpy(itrigger,"95.5",MAXBUF); + + if (Config->ConfValueBool(Config->config_data, "insane","hostmasks", 0)) + return false; + + for (user_hash::iterator u = clientlist->begin(); u != clientlist->end(); u++) + { + if ((match(u->second->MakeHost(),mask.c_str(),true)) || (match(u->second->MakeHostIP(),mask.c_str(),true))) + { + matches++; + } + } + + if (!matches) + return false; + + float percent = ((float)matches / (float)clientlist->size()) * 100; + if (percent > (float)atof(itrigger)) + { + WriteOpers("*** \2WARNING\2: %s tried to set a G/K/E line mask of %s, which covers %.2f%% of the network!",user->nick,mask.c_str(),percent); + return true; + } + return false; +} + +bool InspIRCd::IPMatchesEveryone(const std::string &ip, userrec* user) +{ + char itrigger[MAXBUF]; + long matches = 0; + + if (!Config->ConfValue(Config->config_data, "insane","trigger",0,itrigger,MAXBUF)) + strlcpy(itrigger,"95.5",MAXBUF); + + if (Config->ConfValueBool(Config->config_data, "insane","ipmasks",0)) + return false; + + for (user_hash::iterator u = clientlist->begin(); u != clientlist->end(); u++) + { + if (match(u->second->GetIPString(),ip.c_str(),true)) + matches++; + } + + if (!matches) + return false; + + float percent = ((float)matches / (float)clientlist->size()) * 100; + if (percent > (float)atof(itrigger)) + { + WriteOpers("*** \2WARNING\2: %s tried to set a Z line mask of %s, which covers %.2f%% of the network!",user->nick,ip.c_str(),percent); + return true; + } + return false; +} + +bool InspIRCd::NickMatchesEveryone(const std::string &nick, userrec* user) +{ + char itrigger[MAXBUF]; + long matches = 0; + + if (!Config->ConfValue(Config->config_data, "insane","trigger",0,itrigger,MAXBUF)) + strlcpy(itrigger,"95.5",MAXBUF); + + if (Config->ConfValueBool(Config->config_data, "insane","nickmasks",0)) + return false; + + for (user_hash::iterator u = clientlist->begin(); u != clientlist->end(); u++) + { + if (match(u->second->nick,nick.c_str())) + matches++; + } + + if (!matches) + return false; + + float percent = ((float)matches / (float)clientlist->size()) * 100; + if (percent > (float)atof(itrigger)) + { + WriteOpers("*** \2WARNING\2: %s tried to set a Q line mask of %s, which covers %.2f%% of the network!",user->nick,nick.c_str(),percent); + return true; + } + return false; +} diff --git a/src/configreader.cpp b/src/configreader.cpp index df15d9a61..d4291692f 100644 --- a/src/configreader.cpp +++ b/src/configreader.cpp @@ -1 +1,1714 @@ -/* +------------------------------------+
* | 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 <sstream>
#include <fstream>
#include "xline.h"
#include "exitcodes.h"
#include "commands/cmd_whowas.h"
std::vector<std::string> old_module_names, new_module_names, added_modules, removed_modules;
ServerConfig::ServerConfig(InspIRCd* Instance) : ServerInstance(Instance)
{
this->ClearStack();
*ServerName = *Network = *ServerDesc = *AdminName = '\0';
*HideWhoisServer = *AdminEmail = *AdminNick = *diepass = *restartpass = *FixedQuit = *HideKillsServer = '\0';
*DefaultModes = *CustomVersion = *motd = *rules = *PrefixQuit = *DieValue = *DNSServer = '\0';
*UserStats = *ModPath = *MyExecutable = *DisabledCommands = *PID = *SuffixQuit = '\0';
WhoWasGroupSize = WhoWasMaxGroups = WhoWasMaxKeep = 0;
log_file = NULL;
NoUserDns = forcedebug = OperSpyWhois = nofork = HideBans = HideSplits = UndernetMsgPrefix = false;
CycleHosts = writelog = AllowHalfop = true;
dns_timeout = DieDelay = 5;
MaxTargets = 20;
NetBufferSize = 10240;
SoftLimit = MAXCLIENTS;
MaxConn = SOMAXCONN;
MaxWhoResults = 0;
debugging = 0;
MaxChans = 20;
OperMaxChans = 30;
LogLevel = DEFAULT;
maxbans.clear();
}
void ServerConfig::ClearStack()
{
include_stack.clear();
}
Module* ServerConfig::GetIOHook(int port)
{
std::map<int,Module*>::iterator x = IOHookModule.find(port);
return (x != IOHookModule.end() ? x->second : NULL);
}
Module* ServerConfig::GetIOHook(InspSocket* is)
{
std::map<InspSocket*,Module*>::iterator x = SocketIOHookModule.find(is);
return (x != SocketIOHookModule.end() ? x->second : NULL);
}
bool ServerConfig::AddIOHook(int port, Module* iomod)
{
if (!GetIOHook(port))
{
IOHookModule[port] = iomod;
return true;
}
else
{
throw ModuleException("Port already hooked by another module");
return false;
}
}
bool ServerConfig::AddIOHook(Module* iomod, InspSocket* is)
{
if (!GetIOHook(is))
{
SocketIOHookModule[is] = iomod;
is->IsIOHooked = true;
return true;
}
else
{
throw ModuleException("InspSocket derived class already hooked by another module");
return false;
}
}
bool ServerConfig::DelIOHook(int port)
{
std::map<int,Module*>::iterator x = IOHookModule.find(port);
if (x != IOHookModule.end())
{
IOHookModule.erase(x);
return true;
}
return false;
}
bool ServerConfig::DelIOHook(InspSocket* is)
{
std::map<InspSocket*,Module*>::iterator x = SocketIOHookModule.find(is);
if (x != SocketIOHookModule.end())
{
SocketIOHookModule.erase(x);
return true;
}
return false;
}
void ServerConfig::Update005()
{
std::stringstream out(data005);
std::string token;
std::string line5;
int token_counter = 0;
isupport.clear();
while (out >> token)
{
line5 = line5 + token + " ";
token_counter++;
if (token_counter >= 13)
{
char buf[MAXBUF];
snprintf(buf, MAXBUF, "%s:are supported by this server", line5.c_str());
isupport.push_back(buf);
line5.clear();
token_counter = 0;
}
}
if (!line5.empty())
{
char buf[MAXBUF];
snprintf(buf, MAXBUF, "%s:are supported by this server", line5.c_str());
isupport.push_back(buf);
}
}
void ServerConfig::Send005(userrec* user)
{
for (std::vector<std::string>::iterator line = ServerInstance->Config->isupport.begin(); line != ServerInstance->Config->isupport.end(); line++)
user->WriteServ("005 %s %s", user->nick, line->c_str());
}
bool ServerConfig::CheckOnce(char* tag, bool bail, userrec* user)
{
int count = ConfValueEnum(this->config_data, tag);
if (count > 1)
{
throw CoreException("You have more than one <"+std::string(tag)+"> tag, this is not permitted.");
return false;
}
if (count < 1)
{
throw CoreException("You have not defined a <"+std::string(tag)+"> tag, this is required.");
return false;
}
return true;
}
bool NoValidation(ServerConfig* conf, const char* tag, const char* value, ValueItem &data)
{
return true;
}
bool ValidateMaxTargets(ServerConfig* conf, const char* tag, const char* value, ValueItem &data)
{
if ((data.GetInteger() < 0) || (data.GetInteger() > 31))
{
conf->GetInstance()->Log(DEFAULT,"WARNING: <options:maxtargets> value is greater than 31 or less than 0, set to 20.");
data.Set(20);
}
return true;
}
bool ValidateSoftLimit(ServerConfig* conf, const char* tag, const char* value, ValueItem &data)
{
if ((data.GetInteger() < 1) || (data.GetInteger() > MAXCLIENTS))
{
conf->GetInstance()->Log(DEFAULT,"WARNING: <options:softlimit> value is greater than %d or less than 0, set to %d.",MAXCLIENTS,MAXCLIENTS);
data.Set(MAXCLIENTS);
}
return true;
}
bool ValidateMaxConn(ServerConfig* conf, const char* tag, const char* value, ValueItem &data)
{
if (data.GetInteger() > SOMAXCONN)
conf->GetInstance()->Log(DEFAULT,"WARNING: <options:somaxconn> value may be higher than the system-defined SOMAXCONN value!");
return true;
}
bool InitializeDisabledCommands(const char* data, InspIRCd* ServerInstance)
{
std::stringstream dcmds(data);
std::string thiscmd;
/* Enable everything first */
for (command_table::iterator x = ServerInstance->Parser->cmdlist.begin(); x != ServerInstance->Parser->cmdlist.end(); x++)
x->second->Disable(false);
/* Now disable all the ones which the user wants disabled */
while (dcmds >> thiscmd)
{
command_table::iterator cm = ServerInstance->Parser->cmdlist.find(thiscmd);
if (cm != ServerInstance->Parser->cmdlist.end())
{
cm->second->Disable(true);
}
}
return true;
}
bool ValidateDnsServer(ServerConfig* conf, const char* tag, const char* value, ValueItem &data)
{
if (!*(data.GetString()))
{
std::string nameserver;
#ifdef WINDOWS
conf->GetInstance()->Log(DEFAULT,"WARNING: <dns:server> not defined, attempting to find working server in the registry...");
nameserver = FindNameServerWin();
/* Windows stacks multiple nameservers in one registry key, seperated by commas.
* Spotted by Cataclysm.
*/
if (nameserver.find(',') != std::string::npos)
nameserver = nameserver.substr(0, nameserver.find(','));
data.Set(nameserver.c_str());
conf->GetInstance()->Log(DEFAULT,"<dns:server> set to '%s' as first active resolver in registry.", nameserver.c_str());
#else
// attempt to look up their nameserver from /etc/resolv.conf
conf->GetInstance()->Log(DEFAULT,"WARNING: <dns:server> not defined, attempting to find working server in /etc/resolv.conf...");
ifstream resolv("/etc/resolv.conf");
bool found_server = false;
if (resolv.is_open())
{
while (resolv >> nameserver)
{
if ((nameserver == "nameserver") && (!found_server))
{
resolv >> nameserver;
data.Set(nameserver.c_str());
found_server = true;
conf->GetInstance()->Log(DEFAULT,"<dns:server> set to '%s' as first resolver in /etc/resolv.conf.",nameserver.c_str());
}
}
if (!found_server)
{
conf->GetInstance()->Log(DEFAULT,"/etc/resolv.conf contains no viable nameserver entries! Defaulting to nameserver '127.0.0.1'!");
data.Set("127.0.0.1");
}
}
else
{
conf->GetInstance()->Log(DEFAULT,"/etc/resolv.conf can't be opened! Defaulting to nameserver '127.0.0.1'!");
data.Set("127.0.0.1");
}
#endif
}
return true;
}
bool ValidateServerName(ServerConfig* conf, const char* tag, const char* value, ValueItem &data)
{
/* If we already have a servername, and they changed it, we should throw an exception. */
if ((strcasecmp(conf->ServerName, data.GetString())) && (*conf->ServerName))
{
throw CoreException("Configuration error: You cannot change your servername at runtime! Please restart your server for this change to be applied.");
/* XXX: We don't actually reach this return of course... */
return false;
}
if (!strchr(data.GetString(),'.'))
{
conf->GetInstance()->Log(DEFAULT,"WARNING: <server:name> '%s' is not a fully-qualified domain name. Changed to '%s%c'",data.GetString(),data.GetString(),'.');
std::string moo = std::string(data.GetString()).append(".");
data.Set(moo.c_str());
}
return true;
}
bool ValidateNetBufferSize(ServerConfig* conf, const char* tag, const char* value, ValueItem &data)
{
if ((!data.GetInteger()) || (data.GetInteger() > 65535) || (data.GetInteger() < 1024))
{
conf->GetInstance()->Log(DEFAULT,"No NetBufferSize specified or size out of range, setting to default of 10240.");
data.Set(10240);
}
return true;
}
bool ValidateMaxWho(ServerConfig* conf, const char* tag, const char* value, ValueItem &data)
{
if ((data.GetInteger() > 65535) || (data.GetInteger() < 1))
{
conf->GetInstance()->Log(DEFAULT,"<options:maxwhoresults> size out of range, setting to default of 128.");
data.Set(128);
}
return true;
}
bool ValidateLogLevel(ServerConfig* conf, const char* tag, const char* value, ValueItem &data)
{
std::string dbg = data.GetString();
conf->LogLevel = DEFAULT;
if (dbg == "debug")
conf->LogLevel = DEBUG;
else if (dbg == "verbose")
conf->LogLevel = VERBOSE;
else if (dbg == "default")
conf->LogLevel = DEFAULT;
else if (dbg == "sparse")
conf->LogLevel = SPARSE;
else if (dbg == "none")
conf->LogLevel = NONE;
conf->debugging = (conf->LogLevel == DEBUG);
return true;
}
bool ValidateMotd(ServerConfig* conf, const char* tag, const char* value, ValueItem &data)
{
conf->ReadFile(conf->MOTD, data.GetString());
return true;
}
bool ValidateNotEmpty(ServerConfig* conf, const char* tag, const char* value, ValueItem &data)
{
if (!*data.GetString())
throw CoreException(std::string("The value for ")+tag+" cannot be empty!");
return true;
}
bool ValidateRules(ServerConfig* conf, const char* tag, const char* value, ValueItem &data)
{
conf->ReadFile(conf->RULES, data.GetString());
return true;
}
bool ValidateModeLists(ServerConfig* conf, const char* tag, const char* value, ValueItem &data)
{
memset(conf->HideModeLists, 0, 256);
for (const unsigned char* x = (const unsigned char*)data.GetString(); *x; ++x)
conf->HideModeLists[*x] = true;
return true;
}
bool ValidateExemptChanOps(ServerConfig* conf, const char* tag, const char* value, ValueItem &data)
{
memset(conf->ExemptChanOps, 0, 256);
for (const unsigned char* x = (const unsigned char*)data.GetString(); *x; ++x)
conf->ExemptChanOps[*x] = true;
return true;
}
bool ValidateWhoWas(ServerConfig* conf, const char* tag, const char* value, ValueItem &data)
{
conf->WhoWasMaxKeep = conf->GetInstance()->Duration(data.GetString());
if (conf->WhoWasGroupSize < 0)
conf->WhoWasGroupSize = 0;
if (conf->WhoWasMaxGroups < 0)
conf->WhoWasMaxGroups = 0;
if (conf->WhoWasMaxKeep < 3600)
{
conf->WhoWasMaxKeep = 3600;
conf->GetInstance()->Log(DEFAULT,"WARNING: <whowas:maxkeep> value less than 3600, setting to default 3600");
}
command_t* whowas_command = conf->GetInstance()->Parser->GetHandler("WHOWAS");
if (whowas_command)
{
std::deque<classbase*> params;
whowas_command->HandleInternal(WHOWAS_PRUNE, params);
}
return true;
}
/* Callback called before processing the first <connect> tag
*/
bool InitConnect(ServerConfig* conf, const char* tag)
{
conf->GetInstance()->Log(DEFAULT,"Reading connect classes...");
conf->Classes.clear();
return true;
}
/* Callback called to process a single <connect> tag
*/
bool DoConnect(ServerConfig* conf, const char* tag, char** entries, ValueList &values, int* types)
{
ConnectClass c;
const char* allow = values[0].GetString(); /* Yeah, there are a lot of values. Live with it. */
const char* deny = values[1].GetString();
const char* password = values[2].GetString();
int timeout = values[3].GetInteger();
int pingfreq = values[4].GetInteger();
int flood = values[5].GetInteger();
int threshold = values[6].GetInteger();
int sendq = values[7].GetInteger();
int recvq = values[8].GetInteger();
int localmax = values[9].GetInteger();
int globalmax = values[10].GetInteger();
if (*allow)
{
ConnectClass c(timeout, flood, allow, pingfreq, password, threshold, sendq, recvq, localmax, globalmax);
conf->Classes.push_back(c);
}
else
{
ConnectClass c(deny);
conf->Classes.push_back(c);
}
return true;
}
/* Callback called when there are no more <connect> tags
*/
bool DoneConnect(ServerConfig* conf, const char* tag)
{
return true;
}
/* Callback called before processing the first <uline> tag
*/
bool InitULine(ServerConfig* conf, const char* tag)
{
conf->ulines.clear();
return true;
}
/* Callback called to process a single <uline> tag
*/
bool DoULine(ServerConfig* conf, const char* tag, char** entries, ValueList &values, int* types)
{
const char* server = values[0].GetString();
const bool silent = values[1].GetBool();
conf->ulines[server] = silent;
return true;
}
/* Callback called when there are no more <uline> tags
*/
bool DoneULine(ServerConfig* conf, const char* tag)
{
return true;
}
/* Callback called before processing the first <module> tag
*/
bool InitModule(ServerConfig* conf, const char* tag)
{
old_module_names.clear();
new_module_names.clear();
added_modules.clear();
removed_modules.clear();
for (std::vector<std::string>::iterator t = conf->module_names.begin(); t != conf->module_names.end(); t++)
{
old_module_names.push_back(*t);
}
return true;
}
/* Callback called to process a single <module> tag
*/
bool DoModule(ServerConfig* conf, const char* tag, char** entries, ValueList &values, int* types)
{
const char* modname = values[0].GetString();
new_module_names.push_back(modname);
return true;
}
/* Callback called when there are no more <module> tags
*/
bool DoneModule(ServerConfig* conf, const char* tag)
{
// now create a list of new modules that are due to be loaded
// and a seperate list of modules which are due to be unloaded
for (std::vector<std::string>::iterator _new = new_module_names.begin(); _new != new_module_names.end(); _new++)
{
bool added = true;
for (std::vector<std::string>::iterator old = old_module_names.begin(); old != old_module_names.end(); old++)
{
if (*old == *_new)
added = false;
}
if (added)
added_modules.push_back(*_new);
}
for (std::vector<std::string>::iterator oldm = old_module_names.begin(); oldm != old_module_names.end(); oldm++)
{
bool removed = true;
for (std::vector<std::string>::iterator newm = new_module_names.begin(); newm != new_module_names.end(); newm++)
{
if (*newm == *oldm)
removed = false;
}
if (removed)
removed_modules.push_back(*oldm);
}
return true;
}
/* Callback called before processing the first <banlist> tag
*/
bool InitMaxBans(ServerConfig* conf, const char* tag)
{
conf->maxbans.clear();
return true;
}
/* Callback called to process a single <banlist> tag
*/
bool DoMaxBans(ServerConfig* conf, const char* tag, char** entries, ValueList &values, int* types)
{
const char* channel = values[0].GetString();
int limit = values[1].GetInteger();
conf->maxbans[channel] = limit;
return true;
}
/* Callback called when there are no more <banlist> tags.
*/
bool DoneMaxBans(ServerConfig* conf, const char* tag)
{
return true;
}
void ServerConfig::ReportConfigError(const std::string &errormessage, bool bail, userrec* user)
{
ServerInstance->Log(DEFAULT, "There were errors in your configuration file: %s", errormessage.c_str());
if (bail)
{
/* Unneeded because of the ServerInstance->Log() aboive? */
printf("There were errors in your configuration:\n%s\n\n",errormessage.c_str());
InspIRCd::Exit(EXIT_STATUS_CONFIG);
}
else
{
std::string errors = errormessage;
std::string::size_type start;
unsigned int prefixlen;
start = 0;
/* ":ServerInstance->Config->ServerName NOTICE user->nick :" */
if (user)
{
prefixlen = strlen(this->ServerName) + strlen(user->nick) + 11;
user->WriteServ("NOTICE %s :There were errors in the configuration file:",user->nick);
while (start < errors.length())
{
user->WriteServ("NOTICE %s :%s",user->nick, errors.substr(start, 510 - prefixlen).c_str());
start += 510 - prefixlen;
}
}
else
{
ServerInstance->WriteOpers("There were errors in the configuration file:");
while (start < errors.length())
{
ServerInstance->WriteOpers(errors.substr(start, 360).c_str());
start += 360;
}
}
return;
}
}
void ServerConfig::Read(bool bail, userrec* user)
{
static char debug[MAXBUF]; /* Temporary buffer for debugging value */
static char maxkeep[MAXBUF]; /* Temporary buffer for WhoWasMaxKeep value */
static char hidemodes[MAXBUF]; /* Modes to not allow listing from users below halfop */
static char exemptchanops[MAXBUF]; /* Exempt channel ops from these modes */
int rem = 0, add = 0; /* Number of modules added, number of modules removed */
std::ostringstream errstr; /* String stream containing the error output */
/* These tags MUST occur and must ONLY occur once in the config file */
static char* Once[] = { "server", "admin", "files", "power", "options", NULL };
/* These tags can occur ONCE or not at all */
InitialConfig Values[] = {
{"options", "softlimit", MAXCLIENTS_S, new ValueContainerUInt (&this->SoftLimit), DT_INTEGER, ValidateSoftLimit},
{"options", "somaxconn", SOMAXCONN_S, new ValueContainerInt (&this->MaxConn), DT_INTEGER, ValidateMaxConn},
{"options", "moronbanner", "Youre banned!", new ValueContainerChar (this->MoronBanner), DT_CHARPTR, NoValidation},
{"server", "name", "", new ValueContainerChar (this->ServerName), DT_CHARPTR, ValidateServerName},
{"server", "description", "Configure Me", new ValueContainerChar (this->ServerDesc), DT_CHARPTR, NoValidation},
{"server", "network", "Network", new ValueContainerChar (this->Network), DT_CHARPTR, NoValidation},
{"admin", "name", "", new ValueContainerChar (this->AdminName), DT_CHARPTR, NoValidation},
{"admin", "email", "Mis@configu.red", new ValueContainerChar (this->AdminEmail), DT_CHARPTR, NoValidation},
{"admin", "nick", "Misconfigured", new ValueContainerChar (this->AdminNick), DT_CHARPTR, NoValidation},
{"files", "motd", "", new ValueContainerChar (this->motd), DT_CHARPTR, ValidateMotd},
{"files", "rules", "", new ValueContainerChar (this->rules), DT_CHARPTR, ValidateRules},
{"power", "diepass", "", new ValueContainerChar (this->diepass), DT_CHARPTR, ValidateNotEmpty},
{"power", "pause", "", new ValueContainerInt (&this->DieDelay), DT_INTEGER, NoValidation},
{"power", "restartpass", "", new ValueContainerChar (this->restartpass), DT_CHARPTR, ValidateNotEmpty},
{"options", "prefixquit", "", new ValueContainerChar (this->PrefixQuit), DT_CHARPTR, NoValidation},
{"options", "suffixquit", "", new ValueContainerChar (this->SuffixQuit), DT_CHARPTR, NoValidation},
{"options", "fixedquit", "", new ValueContainerChar (this->FixedQuit), DT_CHARPTR, NoValidation},
{"options", "loglevel", "default", new ValueContainerChar (debug), DT_CHARPTR, ValidateLogLevel},
{"options", "netbuffersize","10240", new ValueContainerInt (&this->NetBufferSize), DT_INTEGER, ValidateNetBufferSize},
{"options", "maxwho", "128", new ValueContainerInt (&this->MaxWhoResults), DT_INTEGER, ValidateMaxWho},
{"options", "allowhalfop", "0", new ValueContainerBool (&this->AllowHalfop), DT_BOOLEAN, NoValidation},
{"dns", "server", "", new ValueContainerChar (this->DNSServer), DT_CHARPTR, ValidateDnsServer},
{"dns", "timeout", "5", new ValueContainerInt (&this->dns_timeout), DT_INTEGER, NoValidation},
{"options", "moduledir", MOD_PATH, new ValueContainerChar (this->ModPath), DT_CHARPTR, NoValidation},
{"disabled", "commands", "", new ValueContainerChar (this->DisabledCommands), DT_CHARPTR, NoValidation},
{"options", "userstats", "", new ValueContainerChar (this->UserStats), DT_CHARPTR, NoValidation},
{"options", "customversion","", new ValueContainerChar (this->CustomVersion), DT_CHARPTR, NoValidation},
{"options", "hidesplits", "0", new ValueContainerBool (&this->HideSplits), DT_BOOLEAN, NoValidation},
{"options", "hidebans", "0", new ValueContainerBool (&this->HideBans), DT_BOOLEAN, NoValidation},
{"options", "hidewhois", "", new ValueContainerChar (this->HideWhoisServer), DT_CHARPTR, NoValidation},
{"options", "hidekills", "", new ValueContainerChar (this->HideKillsServer), DT_CHARPTR, NoValidation},
{"options", "operspywhois", "0", new ValueContainerBool (&this->OperSpyWhois), DT_BOOLEAN, NoValidation},
{"options", "nouserdns", "0", new ValueContainerBool (&this->NoUserDns), DT_BOOLEAN, NoValidation},
{"options", "syntaxhints", "0", new ValueContainerBool (&this->SyntaxHints), DT_BOOLEAN, NoValidation},
{"options", "cyclehosts", "0", new ValueContainerBool (&this->CycleHosts), DT_BOOLEAN, NoValidation},
{"options", "ircumsgprefix","0", new ValueContainerBool (&this->UndernetMsgPrefix), DT_BOOLEAN, NoValidation},
{"options", "announceinvites", "1", new ValueContainerBool (&this->AnnounceInvites), DT_BOOLEAN, NoValidation},
{"options", "hostintopic", "1", new ValueContainerBool (&this->FullHostInTopic), DT_BOOLEAN, NoValidation},
{"options", "hidemodes", "", new ValueContainerChar (hidemodes), DT_CHARPTR, ValidateModeLists},
{"options", "exemptchanops","", new ValueContainerChar (exemptchanops), DT_CHARPTR, ValidateExemptChanOps},
{"options", "defaultmodes", "nt", new ValueContainerChar (this->DefaultModes), DT_CHARPTR, NoValidation},
{"pid", "file", "", new ValueContainerChar (this->PID), DT_CHARPTR, NoValidation},
{"whowas", "groupsize", "10", new ValueContainerInt (&this->WhoWasGroupSize), DT_INTEGER, NoValidation},
{"whowas", "maxgroups", "10240", new ValueContainerInt (&this->WhoWasMaxGroups), DT_INTEGER, NoValidation},
{"whowas", "maxkeep", "3600", new ValueContainerChar (maxkeep), DT_CHARPTR, ValidateWhoWas},
{"die", "value", "", new ValueContainerChar (this->DieValue), DT_CHARPTR, NoValidation},
{"channels", "users", "20", new ValueContainerUInt (&this->MaxChans), DT_INTEGER, NoValidation},
{"channels", "opers", "60", new ValueContainerUInt (&this->OperMaxChans), DT_INTEGER, NoValidation},
{NULL}
};
/* These tags can occur multiple times, and therefore they have special code to read them
* which is different to the code for reading the singular tags listed above.
*/
MultiConfig MultiValues[] = {
{"connect",
{"allow", "deny", "password", "timeout", "pingfreq", "flood",
"threshold", "sendq", "recvq", "localmax", "globalmax", "port",
NULL},
{"", "", "", "", "120", "",
"", "", "", "3", "3", "0",
NULL},
{DT_CHARPTR, DT_CHARPTR, DT_CHARPTR, DT_INTEGER, DT_INTEGER, DT_INTEGER,
DT_INTEGER, DT_INTEGER, DT_INTEGER, DT_INTEGER, DT_INTEGER, DT_INTEGER},
InitConnect, DoConnect, DoneConnect},
{"uline",
{"server", "silent", NULL},
{"", "0", NULL},
{DT_CHARPTR, DT_BOOLEAN},
InitULine,DoULine,DoneULine},
{"banlist",
{"chan", "limit", NULL},
{"", "", NULL},
{DT_CHARPTR, DT_INTEGER},
InitMaxBans, DoMaxBans, DoneMaxBans},
{"module",
{"name", NULL},
{"", NULL},
{DT_CHARPTR},
InitModule, DoModule, DoneModule},
{"badip",
{"reason", "ipmask", NULL},
{"No reason", "", NULL},
{DT_CHARPTR, DT_CHARPTR},
InitXLine, DoZLine, DoneZLine},
{"badnick",
{"reason", "nick", NULL},
{"No reason", "", NULL},
{DT_CHARPTR, DT_CHARPTR},
InitXLine, DoQLine, DoneQLine},
{"badhost",
{"reason", "host", NULL},
{"No reason", "", NULL},
{DT_CHARPTR, DT_CHARPTR},
InitXLine, DoKLine, DoneKLine},
{"exception",
{"reason", "host", NULL},
{"No reason", "", NULL},
{DT_CHARPTR, DT_CHARPTR},
InitXLine, DoELine, DoneELine},
{"type",
{"name", "classes", NULL},
{"", "", NULL},
{DT_CHARPTR, DT_CHARPTR},
InitTypes, DoType, DoneClassesAndTypes},
{"class",
{"name", "commands", NULL},
{"", "", NULL},
{DT_CHARPTR, DT_CHARPTR},
InitClasses, DoClass, DoneClassesAndTypes},
{NULL}
};
include_stack.clear();
/* Load and parse the config file, if there are any errors then explode */
/* Make a copy here so if it fails then we can carry on running with an unaffected config */
ConfigDataHash newconfig;
if (this->LoadConf(newconfig, ServerInstance->ConfigFileName, errstr))
{
/* If we succeeded, set the ircd config to the new one */
this->config_data = newconfig;
}
else
{
ReportConfigError(errstr.str(), bail, user);
return;
}
/* The stuff in here may throw CoreException, be sure we're in a position to catch it. */
try
{
/* Check we dont have more than one of singular tags, or any of them missing
*/
for (int Index = 0; Once[Index]; Index++)
if (!CheckOnce(Once[Index], bail, user))
return;
/* Read the values of all the tags which occur once or not at all, and call their callbacks.
*/
for (int Index = 0; Values[Index].tag; Index++)
{
char item[MAXBUF];
int dt = Values[Index].datatype;
bool allow_newlines = ((dt & DT_ALLOW_NEWLINE) > 0);
dt &= ~DT_ALLOW_NEWLINE;
ConfValue(this->config_data, Values[Index].tag, Values[Index].value, Values[Index].default_value, 0, item, MAXBUF, allow_newlines);
ValueItem vi(item);
if (!Values[Index].validation_function(this, Values[Index].tag, Values[Index].value, vi))
throw CoreException("One or more values in your configuration file failed to validate. Please see your ircd.log for more information.");
switch (Values[Index].datatype)
{
case DT_CHARPTR:
{
ValueContainerChar* vcc = (ValueContainerChar*)Values[Index].val;
/* Make sure we also copy the null terminator */
vcc->Set(vi.GetString(), strlen(vi.GetString()) + 1);
}
break;
case DT_INTEGER:
{
int val = vi.GetInteger();
ValueContainerInt* vci = (ValueContainerInt*)Values[Index].val;
vci->Set(&val, sizeof(int));
}
break;
case DT_BOOLEAN:
{
bool val = vi.GetBool();
ValueContainerBool* vcb = (ValueContainerBool*)Values[Index].val;
vcb->Set(&val, sizeof(bool));
}
break;
default:
/* You don't want to know what happens if someones bad code sends us here. */
break;
}
/* We're done with this now */
delete Values[Index].val;
}
/* Read the multiple-tag items (class tags, connect tags, etc)
* and call the callbacks associated with them. We have three
* callbacks for these, a 'start', 'item' and 'end' callback.
*/
for (int Index = 0; MultiValues[Index].tag; Index++)
{
MultiValues[Index].init_function(this, MultiValues[Index].tag);
int number_of_tags = ConfValueEnum(this->config_data, MultiValues[Index].tag);
for (int tagnum = 0; tagnum < number_of_tags; tagnum++)
{
ValueList vl;
for (int valuenum = 0; MultiValues[Index].items[valuenum]; valuenum++)
{
int dt = MultiValues[Index].datatype[valuenum];
bool allow_newlines = ((dt & DT_ALLOW_NEWLINE) > 0);
dt &= ~DT_ALLOW_NEWLINE;
switch (dt)
{
case DT_CHARPTR:
{
char item[MAXBUF];
if (ConfValue(this->config_data, MultiValues[Index].tag, MultiValues[Index].items[valuenum], MultiValues[Index].items_default[valuenum], tagnum, item, MAXBUF, allow_newlines))
vl.push_back(ValueItem(item));
else
vl.push_back(ValueItem(""));
}
break;
case DT_INTEGER:
{
int item = 0;
if (ConfValueInteger(this->config_data, MultiValues[Index].tag, MultiValues[Index].items[valuenum], MultiValues[Index].items_default[valuenum], tagnum, item))
vl.push_back(ValueItem(item));
else
vl.push_back(ValueItem(0));
}
break;
case DT_BOOLEAN:
{
bool item = ConfValueBool(this->config_data, MultiValues[Index].tag, MultiValues[Index].items[valuenum], MultiValues[Index].items_default[valuenum], tagnum);
vl.push_back(ValueItem(item));
}
break;
default:
/* Someone was smoking craq if we got here, and we're all gonna die. */
break;
}
}
MultiValues[Index].validation_function(this, MultiValues[Index].tag, (char**)MultiValues[Index].items, vl, MultiValues[Index].datatype);
}
MultiValues[Index].finish_function(this, MultiValues[Index].tag);
}
}
catch (CoreException &ce)
{
ReportConfigError(ce.GetReason(), bail, user);
return;
}
// write once here, to try it out and make sure its ok
ServerInstance->WritePID(this->PID);
ServerInstance->Log(DEFAULT,"Done reading configuration file.");
/* If we're rehashing, let's load any new modules, and unload old ones
*/
if (!bail)
{
int found_ports = 0;
FailedPortList pl;
ServerInstance->BindPorts(false, found_ports, pl);
if (pl.size())
{
user->WriteServ("NOTICE %s :*** Not all your client ports could be bound.", user->nick);
user->WriteServ("NOTICE %s :*** The following port(s) failed to bind:", user->nick);
int j = 1;
for (FailedPortList::iterator i = pl.begin(); i != pl.end(); i++, j++)
{
user->WriteServ("NOTICE %s :*** %d. IP: %s Port: %lu", user->nick, j, i->first.empty() ? "<all>" : i->first.c_str(), (unsigned long)i->second);
}
}
if (!removed_modules.empty())
{
for (std::vector<std::string>::iterator removing = removed_modules.begin(); removing != removed_modules.end(); removing++)
{
if (ServerInstance->UnloadModule(removing->c_str()))
{
ServerInstance->WriteOpers("*** REHASH UNLOADED MODULE: %s",removing->c_str());
if (user)
user->WriteServ("973 %s %s :Module %s successfully unloaded.",user->nick, removing->c_str(), removing->c_str());
rem++;
}
else
{
if (user)
user->WriteServ("972 %s %s :Failed to unload module %s: %s",user->nick, removing->c_str(), removing->c_str(), ServerInstance->ModuleError());
}
}
}
if (!added_modules.empty())
{
for (std::vector<std::string>::iterator adding = added_modules.begin(); adding != added_modules.end(); adding++)
{
if (ServerInstance->LoadModule(adding->c_str()))
{
ServerInstance->WriteOpers("*** REHASH LOADED MODULE: %s",adding->c_str());
if (user)
user->WriteServ("975 %s %s :Module %s successfully loaded.",user->nick, adding->c_str(), adding->c_str());
add++;
}
else
{
if (user)
user->WriteServ("974 %s %s :Failed to load module %s: %s",user->nick, adding->c_str(), adding->c_str(), ServerInstance->ModuleError());
}
}
}
ServerInstance->Log(DEFAULT,"Successfully unloaded %lu of %lu modules and loaded %lu of %lu modules.",(unsigned long)rem,(unsigned long)removed_modules.size(),(unsigned long)add,(unsigned long)added_modules.size());
}
if (user)
user->WriteServ("NOTICE %s :*** Successfully rehashed server.", user->nick);
else
ServerInstance->WriteOpers("*** Successfully rehashed server.");
}
bool ServerConfig::LoadConf(ConfigDataHash &target, const char* filename, std::ostringstream &errorstream)
{
std::ifstream conf(filename);
std::string line;
char ch;
long linenumber;
bool in_tag;
bool in_quote;
bool in_comment;
int character_count = 0;
linenumber = 1;
in_tag = false;
in_quote = false;
in_comment = false;
/* Check if the file open failed first */
if (!conf)
{
errorstream << "LoadConf: Couldn't open config file: " << filename << std::endl;
return false;
}
/* Fix the chmod of the file to restrict it to the current user and group */
chmod(filename,0600);
for (unsigned int t = 0; t < include_stack.size(); t++)
{
if (std::string(filename) == include_stack[t])
{
errorstream << "File " << filename << " is included recursively (looped inclusion)." << std::endl;
return false;
}
}
/* It's not already included, add it to the list of files we've loaded */
include_stack.push_back(filename);
/* Start reading characters... */
while(conf.get(ch))
{
/*
* Fix for moronic windows issue spotted by Adremelech.
* Some windows editors save text files as utf-16, which is
* a total pain in the ass to parse. Users should save in the
* right config format! If we ever see a file where the first
* byte is 0xFF or 0xFE, or the second is 0xFF or 0xFE, then
* this is most likely a utf-16 file. Bail out and insult user.
*/
if ((character_count++ < 2) && (ch == '\xFF' || ch == '\xFE'))
{
errorstream << "File " << filename << " cannot be read, as it is encoded in braindead UTF-16. Save your file as plain ASCII!" << std::endl;
return false;
}
/*
* Here we try and get individual tags on separate lines,
* this would be so easy if we just made people format
* their config files like that, but they don't so...
* We check for a '<' and then know the line is over when
* we get a '>' not inside quotes. If we find two '<' and
* no '>' then die with an error.
*/
if((ch == '#') && !in_quote)
in_comment = true;
switch(ch)
{
case '\n':
if (in_quote)
line += '\n';
linenumber++;
case '\r':
if (!in_quote)
in_comment = false;
case '\0':
continue;
case '\t':
ch = ' ';
}
if(in_comment)
continue;
/* XXX: Added by Brain, May 1st 2006 - Escaping of characters.
* Note that this WILL NOT usually allow insertion of newlines,
* because a newline is two characters long. Use it primarily to
* insert the " symbol.
*
* Note that this also involves a further check when parsing the line,
* which can be found below.
*/
if ((ch == '\\') && (in_quote) && (in_tag))
{
line += ch;
char real_character;
if (conf.get(real_character))
{
if (real_character == 'n')
real_character = '\n';
line += real_character;
continue;
}
else
{
errorstream << "End of file after a \\, what did you want to escape?: " << filename << ":" << linenumber << std::endl;
return false;
}
}
if (ch != '\r')
line += ch;
if(ch == '<')
{
if(in_tag)
{
if(!in_quote)
{
errorstream << "Got another opening < when the first one wasn't closed: " << filename << ":" << linenumber << std::endl;
return false;
}
}
else
{
if(in_quote)
{
errorstream << "We're in a quote but outside a tag, interesting. " << filename << ":" << linenumber << std::endl;
return false;
}
else
{
// errorstream << "Opening new config tag on line " << linenumber << std::endl;
in_tag = true;
}
}
}
else if(ch == '"')
{
if(in_tag)
{
if(in_quote)
{
// errorstream << "Closing quote in config tag on line " << linenumber << std::endl;
in_quote = false;
}
else
{
// errorstream << "Opening quote in config tag on line " << linenumber << std::endl;
in_quote = true;
}
}
else
{
if(in_quote)
{
errorstream << "Found a (closing) \" outside a tag: " << filename << ":" << linenumber << std::endl;
}
else
{
errorstream << "Found a (opening) \" outside a tag: " << filename << ":" << linenumber << std::endl;
}
}
}
else if(ch == '>')
{
if(!in_quote)
{
if(in_tag)
{
// errorstream << "Closing config tag on line " << linenumber << std::endl;
in_tag = false;
/*
* If this finds an <include> then ParseLine can simply call
* LoadConf() and load the included config into the same ConfigDataHash
*/
if(!this->ParseLine(target, line, linenumber, errorstream))
return false;
line.clear();
}
else
{
errorstream << "Got a closing > when we weren't inside a tag: " << filename << ":" << linenumber << std::endl;
return false;
}
}
}
}
return true;
}
bool ServerConfig::LoadConf(ConfigDataHash &target, const std::string &filename, std::ostringstream &errorstream)
{
return this->LoadConf(target, filename.c_str(), errorstream);
}
bool ServerConfig::ParseLine(ConfigDataHash &target, std::string &line, long linenumber, std::ostringstream &errorstream)
{
std::string tagname;
std::string current_key;
std::string current_value;
KeyValList results;
bool got_name;
bool got_key;
bool in_quote;
got_name = got_key = in_quote = false;
//std::cout << "ParseLine(data, '" << line << "', " << linenumber << ", stream)" << std::endl;
for(std::string::iterator c = line.begin(); c != line.end(); c++)
{
if(!got_name)
{
/* We don't know the tag name yet. */
if(*c != ' ')
{
if(*c != '<')
{
tagname += *c;
}
}
else
{
/* We got to a space, we should have the tagname now. */
if(tagname.length())
{
got_name = true;
}
}
}
else
{
/* We have the tag name */
if (!got_key)
{
/* We're still reading the key name */
if (*c != '=')
{
if (*c != ' ')
{
current_key += *c;
}
}
else
{
/* We got an '=', end of the key name. */
got_key = true;
}
}
else
{
/* We have the key name, now we're looking for quotes and the value */
/* Correctly handle escaped characters here.
* See the XXX'ed section above.
*/
if ((*c == '\\') && (in_quote))
{
c++;
if (*c == 'n')
current_value += '\n';
else
current_value += *c;
continue;
}
else if ((*c == '\n') && (in_quote))
{
/* Got a 'real' \n, treat it as part of the value */
current_value += '\n';
continue;
}
else if ((*c == '\r') && (in_quote))
/* Got a \r, drop it */
continue;
if (*c == '"')
{
if (!in_quote)
{
/* We're not already in a quote. */
in_quote = true;
}
else
{
/* Leaving quotes, we have the value */
results.push_back(KeyVal(current_key, current_value));
// std::cout << "<" << tagname << ":" << current_key << "> " << current_value << std::endl;
in_quote = false;
got_key = false;
if((tagname == "include") && (current_key == "file"))
{
if(!this->DoInclude(target, current_value, errorstream))
return false;
}
current_key.clear();
current_value.clear();
}
}
else
{
if(in_quote)
{
current_value += *c;
}
}
}
}
}
/* Finished parsing the tag, add it to the config hash */
target.insert(std::pair<std::string, KeyValList > (tagname, results));
return true;
}
bool ServerConfig::DoInclude(ConfigDataHash &target, const std::string &file, std::ostringstream &errorstream)
{
std::string confpath;
std::string newfile;
std::string::size_type pos;
confpath = ServerInstance->ConfigFileName;
newfile = file;
for (std::string::iterator c = newfile.begin(); c != newfile.end(); c++)
{
if (*c == '\\')
{
*c = '/';
}
}
if (file[0] != '/')
{
if((pos = confpath.rfind("/")) != std::string::npos)
{
/* Leaves us with just the path */
newfile = confpath.substr(0, pos) + std::string("/") + newfile;
}
else
{
errorstream << "Couldn't get config path from: " << confpath << std::endl;
return false;
}
}
return LoadConf(target, newfile, errorstream);
}
bool ServerConfig::ConfValue(ConfigDataHash &target, const char* tag, const char* var, int index, char* result, int length, bool allow_linefeeds)
{
return ConfValue(target, tag, var, "", index, result, length, allow_linefeeds);
}
bool ServerConfig::ConfValue(ConfigDataHash &target, const char* tag, const char* var, const char* default_value, int index, char* result, int length, bool allow_linefeeds)
{
std::string value;
bool r = ConfValue(target, std::string(tag), std::string(var), std::string(default_value), index, value, allow_linefeeds);
strlcpy(result, value.c_str(), length);
return r;
}
bool ServerConfig::ConfValue(ConfigDataHash &target, const std::string &tag, const std::string &var, int index, std::string &result, bool allow_linefeeds)
{
return ConfValue(target, tag, var, "", index, result, allow_linefeeds);
}
bool ServerConfig::ConfValue(ConfigDataHash &target, const std::string &tag, const std::string &var, const std::string &default_value, int index, std::string &result, bool allow_linefeeds)
{
ConfigDataHash::size_type pos = index;
if((pos >= 0) && (pos < target.count(tag)))
{
ConfigDataHash::iterator iter = target.find(tag);
for(int i = 0; i < index; i++)
iter++;
for(KeyValList::iterator j = iter->second.begin(); j != iter->second.end(); j++)
{
if(j->first == var)
{
if ((!allow_linefeeds) && (j->second.find('\n') != std::string::npos))
{
ServerInstance->Log(DEFAULT, "Value of <" + tag + ":" + var+ "> contains a linefeed, and linefeeds in this value are not permitted -- stripped to spaces.");
for (std::string::iterator n = j->second.begin(); n != j->second.end(); n++)
if (*n == '\n')
*n = ' ';
}
else
{
result = j->second;
return true;
}
}
}
if (!default_value.empty())
{
result = default_value;
return true;
}
}
else if(pos == 0)
{
if (!default_value.empty())
{
result = default_value;
return true;
}
}
return false;
}
bool ServerConfig::ConfValueInteger(ConfigDataHash &target, const char* tag, const char* var, int index, int &result)
{
return ConfValueInteger(target, std::string(tag), std::string(var), "", index, result);
}
bool ServerConfig::ConfValueInteger(ConfigDataHash &target, const char* tag, const char* var, const char* default_value, int index, int &result)
{
return ConfValueInteger(target, std::string(tag), std::string(var), std::string(default_value), index, result);
}
bool ServerConfig::ConfValueInteger(ConfigDataHash &target, const std::string &tag, const std::string &var, int index, int &result)
{
return ConfValueInteger(target, tag, var, "", index, result);
}
bool ServerConfig::ConfValueInteger(ConfigDataHash &target, const std::string &tag, const std::string &var, const std::string &default_value, int index, int &result)
{
std::string value;
std::istringstream stream;
bool r = ConfValue(target, tag, var, default_value, index, value);
stream.str(value);
if(!(stream >> result))
return false;
else
{
if (!value.empty())
{
if (value.substr(0,2) == "0x")
{
char* endptr;
value.erase(0,2);
result = strtol(value.c_str(), &endptr, 16);
/* No digits found */
if (endptr == value.c_str())
return false;
}
else
{
char denominator = *(value.end() - 1);
switch (toupper(denominator))
{
case 'K':
/* Kilobytes -> bytes */
result = result * 1024;
break;
case 'M':
/* Megabytes -> bytes */
result = result * 1024 * 1024;
break;
case 'G':
/* Gigabytes -> bytes */
result = result * 1024 * 1024 * 1024;
break;
}
}
}
}
return r;
}
bool ServerConfig::ConfValueBool(ConfigDataHash &target, const char* tag, const char* var, int index)
{
return ConfValueBool(target, std::string(tag), std::string(var), "", index);
}
bool ServerConfig::ConfValueBool(ConfigDataHash &target, const char* tag, const char* var, const char* default_value, int index)
{
return ConfValueBool(target, std::string(tag), std::string(var), std::string(default_value), index);
}
bool ServerConfig::ConfValueBool(ConfigDataHash &target, const std::string &tag, const std::string &var, int index)
{
return ConfValueBool(target, tag, var, "", index);
}
bool ServerConfig::ConfValueBool(ConfigDataHash &target, const std::string &tag, const std::string &var, const std::string &default_value, int index)
{
std::string result;
if(!ConfValue(target, tag, var, default_value, index, result))
return false;
return ((result == "yes") || (result == "true") || (result == "1"));
}
int ServerConfig::ConfValueEnum(ConfigDataHash &target, const char* tag)
{
return target.count(tag);
}
int ServerConfig::ConfValueEnum(ConfigDataHash &target, const std::string &tag)
{
return target.count(tag);
}
int ServerConfig::ConfVarEnum(ConfigDataHash &target, const char* tag, int index)
{
return ConfVarEnum(target, std::string(tag), index);
}
int ServerConfig::ConfVarEnum(ConfigDataHash &target, const std::string &tag, int index)
{
ConfigDataHash::size_type pos = index;
if((pos >= 0) && (pos < target.count(tag)))
{
ConfigDataHash::const_iterator iter = target.find(tag);
for(int i = 0; i < index; i++)
iter++;
return iter->second.size();
}
return 0;
}
/** Read the contents of a file located by `fname' into a file_cache pointed at by `F'.
*/
bool ServerConfig::ReadFile(file_cache &F, const char* fname)
{
if (!fname || !*fname)
return false;
FILE* file = NULL;
char linebuf[MAXBUF];
F.clear();
if ((*fname != '/') && (*fname != '\\'))
{
std::string::size_type pos;
std::string confpath = ServerInstance->ConfigFileName;
std::string newfile = fname;
if ((pos = confpath.rfind("/")) != std::string::npos)
newfile = confpath.substr(0, pos) + std::string("/") + fname;
else if ((pos = confpath.rfind("\\")) != std::string::npos)
newfile = confpath.substr(0, pos) + std::string("\\") + fname;
if (!FileExists(newfile.c_str()))
return false;
file = fopen(newfile.c_str(), "r");
}
else
{
if (!FileExists(fname))
return false;
file = fopen(fname, "r");
}
if (file)
{
while (!feof(file))
{
if (fgets(linebuf, sizeof(linebuf), file))
linebuf[strlen(linebuf)-1] = 0;
else
*linebuf = 0;
if (!feof(file))
{
F.push_back(*linebuf ? linebuf : " ");
}
}
fclose(file);
}
else
return false;
return true;
}
bool ServerConfig::FileExists(const char* file)
{
struct stat sb;
if (stat(file, &sb) == -1)
return false;
if ((sb.st_mode & S_IFDIR) > 0)
return false;
FILE *input;
if ((input = fopen (file, "r")) == NULL)
return false;
else
{
fclose(input);
return true;
}
}
char* ServerConfig::CleanFilename(char* name)
{
char* p = name + strlen(name);
while ((p != name) && (*p != '/') && (*p != '\\')) p--;
return (p != name ? ++p : p);
}
bool ServerConfig::DirValid(const char* dirandfile)
{
#ifdef WINDOWS
return true;
#endif
char work[1024];
char buffer[1024];
char otherdir[1024];
int p;
strlcpy(work, dirandfile, 1024);
p = strlen(work);
// we just want the dir
while (*work)
{
if (work[p] == '/')
{
work[p] = '\0';
break;
}
work[p--] = '\0';
}
// Get the current working directory
if (getcwd(buffer, 1024 ) == NULL )
return false;
if (chdir(work) == -1)
return false;
if (getcwd(otherdir, 1024 ) == NULL )
return false;
if (chdir(buffer) == -1)
return false;
size_t t = strlen(work);
if (strlen(otherdir) >= t)
{
otherdir[t] = '\0';
if (!strcmp(otherdir,work))
{
return true;
}
return false;
}
else
{
return false;
}
}
std::string ServerConfig::GetFullProgDir()
{
char buffer[PATH_MAX+1];
#ifdef WINDOWS
/* Windows has specific api calls to get the exe path that never fail.
* For once, windows has something of use, compared to the POSIX code
* for this, this is positively neato.
*/
if (GetModuleFileName(NULL, buffer, MAX_PATH))
{
std::string fullpath = buffer;
std::string::size_type n = fullpath.rfind("\\inspircd.exe");
return std::string(fullpath, 0, n);
}
#else
// Get the current working directory
if (getcwd(buffer, PATH_MAX))
{
std::string remainder = this->argv[0];
/* Does argv[0] start with /? its a full path, use it */
if (remainder[0] == '/')
{
std::string::size_type n = remainder.rfind("/inspircd");
return std::string(remainder, 0, n);
}
std::string fullpath = std::string(buffer) + "/" + remainder;
std::string::size_type n = fullpath.rfind("/inspircd");
return std::string(fullpath, 0, n);
}
#endif
return "/";
}
InspIRCd* ServerConfig::GetInstance()
{
return ServerInstance;
}
ValueItem::ValueItem(int value)
{
std::stringstream n;
n << value;
v = n.str();
}
ValueItem::ValueItem(bool value)
{
std::stringstream n;
n << value;
v = n.str();
}
ValueItem::ValueItem(char* value)
{
v = value;
}
void ValueItem::Set(char* value)
{
v = value;
}
void ValueItem::Set(const char* value)
{
v = value;
}
void ValueItem::Set(int value)
{
std::stringstream n;
n << value;
v = n.str();
}
int ValueItem::GetInteger()
{
if (v.empty())
return 0;
return atoi(v.c_str());
}
char* ValueItem::GetString()
{
return (char*)v.c_str();
}
bool ValueItem::GetBool()
{
return (GetInteger() || v == "yes" || v == "true");
}
\ No newline at end of file +/* +------------------------------------+ + * | 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 <sstream> +#include <fstream> +#include "xline.h" +#include "exitcodes.h" +#include "commands/cmd_whowas.h" + +std::vector<std::string> old_module_names, new_module_names, added_modules, removed_modules; + +ServerConfig::ServerConfig(InspIRCd* Instance) : ServerInstance(Instance) +{ + this->ClearStack(); + *ServerName = *Network = *ServerDesc = *AdminName = '\0'; + *HideWhoisServer = *AdminEmail = *AdminNick = *diepass = *restartpass = *FixedQuit = *HideKillsServer = '\0'; + *DefaultModes = *CustomVersion = *motd = *rules = *PrefixQuit = *DieValue = *DNSServer = '\0'; + *UserStats = *ModPath = *MyExecutable = *DisabledCommands = *PID = *SuffixQuit = '\0'; + WhoWasGroupSize = WhoWasMaxGroups = WhoWasMaxKeep = 0; + log_file = NULL; + NoUserDns = forcedebug = OperSpyWhois = nofork = HideBans = HideSplits = UndernetMsgPrefix = false; + CycleHosts = writelog = AllowHalfop = true; + dns_timeout = DieDelay = 5; + MaxTargets = 20; + NetBufferSize = 10240; + SoftLimit = MAXCLIENTS; + MaxConn = SOMAXCONN; + MaxWhoResults = 0; + debugging = 0; + MaxChans = 20; + OperMaxChans = 30; + LogLevel = DEFAULT; + maxbans.clear(); +} + +void ServerConfig::ClearStack() +{ + include_stack.clear(); +} + +Module* ServerConfig::GetIOHook(int port) +{ + std::map<int,Module*>::iterator x = IOHookModule.find(port); + return (x != IOHookModule.end() ? x->second : NULL); +} + +Module* ServerConfig::GetIOHook(InspSocket* is) +{ + std::map<InspSocket*,Module*>::iterator x = SocketIOHookModule.find(is); + return (x != SocketIOHookModule.end() ? x->second : NULL); +} + +bool ServerConfig::AddIOHook(int port, Module* iomod) +{ + if (!GetIOHook(port)) + { + IOHookModule[port] = iomod; + return true; + } + else + { + throw ModuleException("Port already hooked by another module"); + return false; + } +} + +bool ServerConfig::AddIOHook(Module* iomod, InspSocket* is) +{ + if (!GetIOHook(is)) + { + SocketIOHookModule[is] = iomod; + is->IsIOHooked = true; + return true; + } + else + { + throw ModuleException("InspSocket derived class already hooked by another module"); + return false; + } +} + +bool ServerConfig::DelIOHook(int port) +{ + std::map<int,Module*>::iterator x = IOHookModule.find(port); + if (x != IOHookModule.end()) + { + IOHookModule.erase(x); + return true; + } + return false; +} + +bool ServerConfig::DelIOHook(InspSocket* is) +{ + std::map<InspSocket*,Module*>::iterator x = SocketIOHookModule.find(is); + if (x != SocketIOHookModule.end()) + { + SocketIOHookModule.erase(x); + return true; + } + return false; +} + +void ServerConfig::Update005() +{ + std::stringstream out(data005); + std::string token; + std::string line5; + int token_counter = 0; + isupport.clear(); + while (out >> token) + { + line5 = line5 + token + " "; + token_counter++; + if (token_counter >= 13) + { + char buf[MAXBUF]; + snprintf(buf, MAXBUF, "%s:are supported by this server", line5.c_str()); + isupport.push_back(buf); + line5.clear(); + token_counter = 0; + } + } + if (!line5.empty()) + { + char buf[MAXBUF]; + snprintf(buf, MAXBUF, "%s:are supported by this server", line5.c_str()); + isupport.push_back(buf); + } +} + +void ServerConfig::Send005(userrec* user) +{ + for (std::vector<std::string>::iterator line = ServerInstance->Config->isupport.begin(); line != ServerInstance->Config->isupport.end(); line++) + user->WriteServ("005 %s %s", user->nick, line->c_str()); +} + +bool ServerConfig::CheckOnce(char* tag, bool bail, userrec* user) +{ + int count = ConfValueEnum(this->config_data, tag); + + if (count > 1) + { + throw CoreException("You have more than one <"+std::string(tag)+"> tag, this is not permitted."); + return false; + } + if (count < 1) + { + throw CoreException("You have not defined a <"+std::string(tag)+"> tag, this is required."); + return false; + } + return true; +} + +bool NoValidation(ServerConfig* conf, const char* tag, const char* value, ValueItem &data) +{ + return true; +} + +bool ValidateMaxTargets(ServerConfig* conf, const char* tag, const char* value, ValueItem &data) +{ + if ((data.GetInteger() < 0) || (data.GetInteger() > 31)) + { + conf->GetInstance()->Log(DEFAULT,"WARNING: <options:maxtargets> value is greater than 31 or less than 0, set to 20."); + data.Set(20); + } + return true; +} + +bool ValidateSoftLimit(ServerConfig* conf, const char* tag, const char* value, ValueItem &data) +{ + if ((data.GetInteger() < 1) || (data.GetInteger() > MAXCLIENTS)) + { + conf->GetInstance()->Log(DEFAULT,"WARNING: <options:softlimit> value is greater than %d or less than 0, set to %d.",MAXCLIENTS,MAXCLIENTS); + data.Set(MAXCLIENTS); + } + return true; +} + +bool ValidateMaxConn(ServerConfig* conf, const char* tag, const char* value, ValueItem &data) +{ + if (data.GetInteger() > SOMAXCONN) + conf->GetInstance()->Log(DEFAULT,"WARNING: <options:somaxconn> value may be higher than the system-defined SOMAXCONN value!"); + return true; +} + +bool InitializeDisabledCommands(const char* data, InspIRCd* ServerInstance) +{ + std::stringstream dcmds(data); + std::string thiscmd; + + /* Enable everything first */ + for (command_table::iterator x = ServerInstance->Parser->cmdlist.begin(); x != ServerInstance->Parser->cmdlist.end(); x++) + x->second->Disable(false); + + /* Now disable all the ones which the user wants disabled */ + while (dcmds >> thiscmd) + { + command_table::iterator cm = ServerInstance->Parser->cmdlist.find(thiscmd); + if (cm != ServerInstance->Parser->cmdlist.end()) + { + cm->second->Disable(true); + } + } + return true; +} + +bool ValidateDnsServer(ServerConfig* conf, const char* tag, const char* value, ValueItem &data) +{ + if (!*(data.GetString())) + { + std::string nameserver; +#ifdef WINDOWS + conf->GetInstance()->Log(DEFAULT,"WARNING: <dns:server> not defined, attempting to find working server in the registry..."); + nameserver = FindNameServerWin(); + /* Windows stacks multiple nameservers in one registry key, seperated by commas. + * Spotted by Cataclysm. + */ + if (nameserver.find(',') != std::string::npos) + nameserver = nameserver.substr(0, nameserver.find(',')); + data.Set(nameserver.c_str()); + conf->GetInstance()->Log(DEFAULT,"<dns:server> set to '%s' as first active resolver in registry.", nameserver.c_str()); +#else + // attempt to look up their nameserver from /etc/resolv.conf + conf->GetInstance()->Log(DEFAULT,"WARNING: <dns:server> not defined, attempting to find working server in /etc/resolv.conf..."); + ifstream resolv("/etc/resolv.conf"); + bool found_server = false; + + if (resolv.is_open()) + { + while (resolv >> nameserver) + { + if ((nameserver == "nameserver") && (!found_server)) + { + resolv >> nameserver; + data.Set(nameserver.c_str()); + found_server = true; + conf->GetInstance()->Log(DEFAULT,"<dns:server> set to '%s' as first resolver in /etc/resolv.conf.",nameserver.c_str()); + } + } + + if (!found_server) + { + conf->GetInstance()->Log(DEFAULT,"/etc/resolv.conf contains no viable nameserver entries! Defaulting to nameserver '127.0.0.1'!"); + data.Set("127.0.0.1"); + } + } + else + { + conf->GetInstance()->Log(DEFAULT,"/etc/resolv.conf can't be opened! Defaulting to nameserver '127.0.0.1'!"); + data.Set("127.0.0.1"); + } +#endif + } + return true; +} + +bool ValidateServerName(ServerConfig* conf, const char* tag, const char* value, ValueItem &data) +{ + /* If we already have a servername, and they changed it, we should throw an exception. */ + if ((strcasecmp(conf->ServerName, data.GetString())) && (*conf->ServerName)) + { + throw CoreException("Configuration error: You cannot change your servername at runtime! Please restart your server for this change to be applied."); + /* XXX: We don't actually reach this return of course... */ + return false; + } + if (!strchr(data.GetString(),'.')) + { + conf->GetInstance()->Log(DEFAULT,"WARNING: <server:name> '%s' is not a fully-qualified domain name. Changed to '%s%c'",data.GetString(),data.GetString(),'.'); + std::string moo = std::string(data.GetString()).append("."); + data.Set(moo.c_str()); + } + return true; +} + +bool ValidateNetBufferSize(ServerConfig* conf, const char* tag, const char* value, ValueItem &data) +{ + if ((!data.GetInteger()) || (data.GetInteger() > 65535) || (data.GetInteger() < 1024)) + { + conf->GetInstance()->Log(DEFAULT,"No NetBufferSize specified or size out of range, setting to default of 10240."); + data.Set(10240); + } + return true; +} + +bool ValidateMaxWho(ServerConfig* conf, const char* tag, const char* value, ValueItem &data) +{ + if ((data.GetInteger() > 65535) || (data.GetInteger() < 1)) + { + conf->GetInstance()->Log(DEFAULT,"<options:maxwhoresults> size out of range, setting to default of 128."); + data.Set(128); + } + return true; +} + +bool ValidateLogLevel(ServerConfig* conf, const char* tag, const char* value, ValueItem &data) +{ + std::string dbg = data.GetString(); + conf->LogLevel = DEFAULT; + + if (dbg == "debug") + conf->LogLevel = DEBUG; + else if (dbg == "verbose") + conf->LogLevel = VERBOSE; + else if (dbg == "default") + conf->LogLevel = DEFAULT; + else if (dbg == "sparse") + conf->LogLevel = SPARSE; + else if (dbg == "none") + conf->LogLevel = NONE; + + conf->debugging = (conf->LogLevel == DEBUG); + + return true; +} + +bool ValidateMotd(ServerConfig* conf, const char* tag, const char* value, ValueItem &data) +{ + conf->ReadFile(conf->MOTD, data.GetString()); + return true; +} + +bool ValidateNotEmpty(ServerConfig* conf, const char* tag, const char* value, ValueItem &data) +{ + if (!*data.GetString()) + throw CoreException(std::string("The value for ")+tag+" cannot be empty!"); + return true; +} + +bool ValidateRules(ServerConfig* conf, const char* tag, const char* value, ValueItem &data) +{ + conf->ReadFile(conf->RULES, data.GetString()); + return true; +} + +bool ValidateModeLists(ServerConfig* conf, const char* tag, const char* value, ValueItem &data) +{ + memset(conf->HideModeLists, 0, 256); + for (const unsigned char* x = (const unsigned char*)data.GetString(); *x; ++x) + conf->HideModeLists[*x] = true; + return true; +} + +bool ValidateExemptChanOps(ServerConfig* conf, const char* tag, const char* value, ValueItem &data) +{ + memset(conf->ExemptChanOps, 0, 256); + for (const unsigned char* x = (const unsigned char*)data.GetString(); *x; ++x) + conf->ExemptChanOps[*x] = true; + return true; +} + +bool ValidateWhoWas(ServerConfig* conf, const char* tag, const char* value, ValueItem &data) +{ + conf->WhoWasMaxKeep = conf->GetInstance()->Duration(data.GetString()); + + if (conf->WhoWasGroupSize < 0) + conf->WhoWasGroupSize = 0; + + if (conf->WhoWasMaxGroups < 0) + conf->WhoWasMaxGroups = 0; + + if (conf->WhoWasMaxKeep < 3600) + { + conf->WhoWasMaxKeep = 3600; + conf->GetInstance()->Log(DEFAULT,"WARNING: <whowas:maxkeep> value less than 3600, setting to default 3600"); + } + + command_t* whowas_command = conf->GetInstance()->Parser->GetHandler("WHOWAS"); + if (whowas_command) + { + std::deque<classbase*> params; + whowas_command->HandleInternal(WHOWAS_PRUNE, params); + } + + return true; +} + +/* Callback called before processing the first <connect> tag + */ +bool InitConnect(ServerConfig* conf, const char* tag) +{ + conf->GetInstance()->Log(DEFAULT,"Reading connect classes..."); + conf->Classes.clear(); + return true; +} + +/* Callback called to process a single <connect> tag + */ +bool DoConnect(ServerConfig* conf, const char* tag, char** entries, ValueList &values, int* types) +{ + ConnectClass c; + const char* allow = values[0].GetString(); /* Yeah, there are a lot of values. Live with it. */ + const char* deny = values[1].GetString(); + const char* password = values[2].GetString(); + int timeout = values[3].GetInteger(); + int pingfreq = values[4].GetInteger(); + int flood = values[5].GetInteger(); + int threshold = values[6].GetInteger(); + int sendq = values[7].GetInteger(); + int recvq = values[8].GetInteger(); + int localmax = values[9].GetInteger(); + int globalmax = values[10].GetInteger(); + + if (*allow) + { + ConnectClass c(timeout, flood, allow, pingfreq, password, threshold, sendq, recvq, localmax, globalmax); + conf->Classes.push_back(c); + } + else + { + ConnectClass c(deny); + conf->Classes.push_back(c); + } + + return true; +} + +/* Callback called when there are no more <connect> tags + */ +bool DoneConnect(ServerConfig* conf, const char* tag) +{ + return true; +} + +/* Callback called before processing the first <uline> tag + */ +bool InitULine(ServerConfig* conf, const char* tag) +{ + conf->ulines.clear(); + return true; +} + +/* Callback called to process a single <uline> tag + */ +bool DoULine(ServerConfig* conf, const char* tag, char** entries, ValueList &values, int* types) +{ + const char* server = values[0].GetString(); + const bool silent = values[1].GetBool(); + conf->ulines[server] = silent; + return true; +} + +/* Callback called when there are no more <uline> tags + */ +bool DoneULine(ServerConfig* conf, const char* tag) +{ + return true; +} + +/* Callback called before processing the first <module> tag + */ +bool InitModule(ServerConfig* conf, const char* tag) +{ + old_module_names.clear(); + new_module_names.clear(); + added_modules.clear(); + removed_modules.clear(); + for (std::vector<std::string>::iterator t = conf->module_names.begin(); t != conf->module_names.end(); t++) + { + old_module_names.push_back(*t); + } + return true; +} + +/* Callback called to process a single <module> tag + */ +bool DoModule(ServerConfig* conf, const char* tag, char** entries, ValueList &values, int* types) +{ + const char* modname = values[0].GetString(); + new_module_names.push_back(modname); + return true; +} + +/* Callback called when there are no more <module> tags + */ +bool DoneModule(ServerConfig* conf, const char* tag) +{ + // now create a list of new modules that are due to be loaded + // and a seperate list of modules which are due to be unloaded + for (std::vector<std::string>::iterator _new = new_module_names.begin(); _new != new_module_names.end(); _new++) + { + bool added = true; + + for (std::vector<std::string>::iterator old = old_module_names.begin(); old != old_module_names.end(); old++) + { + if (*old == *_new) + added = false; + } + + if (added) + added_modules.push_back(*_new); + } + + for (std::vector<std::string>::iterator oldm = old_module_names.begin(); oldm != old_module_names.end(); oldm++) + { + bool removed = true; + for (std::vector<std::string>::iterator newm = new_module_names.begin(); newm != new_module_names.end(); newm++) + { + if (*newm == *oldm) + removed = false; + } + + if (removed) + removed_modules.push_back(*oldm); + } + return true; +} + +/* Callback called before processing the first <banlist> tag + */ +bool InitMaxBans(ServerConfig* conf, const char* tag) +{ + conf->maxbans.clear(); + return true; +} + +/* Callback called to process a single <banlist> tag + */ +bool DoMaxBans(ServerConfig* conf, const char* tag, char** entries, ValueList &values, int* types) +{ + const char* channel = values[0].GetString(); + int limit = values[1].GetInteger(); + conf->maxbans[channel] = limit; + return true; +} + +/* Callback called when there are no more <banlist> tags. + */ +bool DoneMaxBans(ServerConfig* conf, const char* tag) +{ + return true; +} + +void ServerConfig::ReportConfigError(const std::string &errormessage, bool bail, userrec* user) +{ + ServerInstance->Log(DEFAULT, "There were errors in your configuration file: %s", errormessage.c_str()); + if (bail) + { + /* Unneeded because of the ServerInstance->Log() aboive? */ + printf("There were errors in your configuration:\n%s\n\n",errormessage.c_str()); + InspIRCd::Exit(EXIT_STATUS_CONFIG); + } + else + { + std::string errors = errormessage; + std::string::size_type start; + unsigned int prefixlen; + start = 0; + /* ":ServerInstance->Config->ServerName NOTICE user->nick :" */ + if (user) + { + prefixlen = strlen(this->ServerName) + strlen(user->nick) + 11; + user->WriteServ("NOTICE %s :There were errors in the configuration file:",user->nick); + while (start < errors.length()) + { + user->WriteServ("NOTICE %s :%s",user->nick, errors.substr(start, 510 - prefixlen).c_str()); + start += 510 - prefixlen; + } + } + else + { + ServerInstance->WriteOpers("There were errors in the configuration file:"); + while (start < errors.length()) + { + ServerInstance->WriteOpers(errors.substr(start, 360).c_str()); + start += 360; + } + } + return; + } +} + +void ServerConfig::Read(bool bail, userrec* user) +{ + static char debug[MAXBUF]; /* Temporary buffer for debugging value */ + static char maxkeep[MAXBUF]; /* Temporary buffer for WhoWasMaxKeep value */ + static char hidemodes[MAXBUF]; /* Modes to not allow listing from users below halfop */ + static char exemptchanops[MAXBUF]; /* Exempt channel ops from these modes */ + int rem = 0, add = 0; /* Number of modules added, number of modules removed */ + std::ostringstream errstr; /* String stream containing the error output */ + + /* These tags MUST occur and must ONLY occur once in the config file */ + static char* Once[] = { "server", "admin", "files", "power", "options", NULL }; + + /* These tags can occur ONCE or not at all */ + InitialConfig Values[] = { + {"options", "softlimit", MAXCLIENTS_S, new ValueContainerUInt (&this->SoftLimit), DT_INTEGER, ValidateSoftLimit}, + {"options", "somaxconn", SOMAXCONN_S, new ValueContainerInt (&this->MaxConn), DT_INTEGER, ValidateMaxConn}, + {"options", "moronbanner", "Youre banned!", new ValueContainerChar (this->MoronBanner), DT_CHARPTR, NoValidation}, + {"server", "name", "", new ValueContainerChar (this->ServerName), DT_CHARPTR, ValidateServerName}, + {"server", "description", "Configure Me", new ValueContainerChar (this->ServerDesc), DT_CHARPTR, NoValidation}, + {"server", "network", "Network", new ValueContainerChar (this->Network), DT_CHARPTR, NoValidation}, + {"admin", "name", "", new ValueContainerChar (this->AdminName), DT_CHARPTR, NoValidation}, + {"admin", "email", "Mis@configu.red", new ValueContainerChar (this->AdminEmail), DT_CHARPTR, NoValidation}, + {"admin", "nick", "Misconfigured", new ValueContainerChar (this->AdminNick), DT_CHARPTR, NoValidation}, + {"files", "motd", "", new ValueContainerChar (this->motd), DT_CHARPTR, ValidateMotd}, + {"files", "rules", "", new ValueContainerChar (this->rules), DT_CHARPTR, ValidateRules}, + {"power", "diepass", "", new ValueContainerChar (this->diepass), DT_CHARPTR, ValidateNotEmpty}, + {"power", "pause", "", new ValueContainerInt (&this->DieDelay), DT_INTEGER, NoValidation}, + {"power", "restartpass", "", new ValueContainerChar (this->restartpass), DT_CHARPTR, ValidateNotEmpty}, + {"options", "prefixquit", "", new ValueContainerChar (this->PrefixQuit), DT_CHARPTR, NoValidation}, + {"options", "suffixquit", "", new ValueContainerChar (this->SuffixQuit), DT_CHARPTR, NoValidation}, + {"options", "fixedquit", "", new ValueContainerChar (this->FixedQuit), DT_CHARPTR, NoValidation}, + {"options", "loglevel", "default", new ValueContainerChar (debug), DT_CHARPTR, ValidateLogLevel}, + {"options", "netbuffersize","10240", new ValueContainerInt (&this->NetBufferSize), DT_INTEGER, ValidateNetBufferSize}, + {"options", "maxwho", "128", new ValueContainerInt (&this->MaxWhoResults), DT_INTEGER, ValidateMaxWho}, + {"options", "allowhalfop", "0", new ValueContainerBool (&this->AllowHalfop), DT_BOOLEAN, NoValidation}, + {"dns", "server", "", new ValueContainerChar (this->DNSServer), DT_CHARPTR, ValidateDnsServer}, + {"dns", "timeout", "5", new ValueContainerInt (&this->dns_timeout), DT_INTEGER, NoValidation}, + {"options", "moduledir", MOD_PATH, new ValueContainerChar (this->ModPath), DT_CHARPTR, NoValidation}, + {"disabled", "commands", "", new ValueContainerChar (this->DisabledCommands), DT_CHARPTR, NoValidation}, + {"options", "userstats", "", new ValueContainerChar (this->UserStats), DT_CHARPTR, NoValidation}, + {"options", "customversion","", new ValueContainerChar (this->CustomVersion), DT_CHARPTR, NoValidation}, + {"options", "hidesplits", "0", new ValueContainerBool (&this->HideSplits), DT_BOOLEAN, NoValidation}, + {"options", "hidebans", "0", new ValueContainerBool (&this->HideBans), DT_BOOLEAN, NoValidation}, + {"options", "hidewhois", "", new ValueContainerChar (this->HideWhoisServer), DT_CHARPTR, NoValidation}, + {"options", "hidekills", "", new ValueContainerChar (this->HideKillsServer), DT_CHARPTR, NoValidation}, + {"options", "operspywhois", "0", new ValueContainerBool (&this->OperSpyWhois), DT_BOOLEAN, NoValidation}, + {"options", "nouserdns", "0", new ValueContainerBool (&this->NoUserDns), DT_BOOLEAN, NoValidation}, + {"options", "syntaxhints", "0", new ValueContainerBool (&this->SyntaxHints), DT_BOOLEAN, NoValidation}, + {"options", "cyclehosts", "0", new ValueContainerBool (&this->CycleHosts), DT_BOOLEAN, NoValidation}, + {"options", "ircumsgprefix","0", new ValueContainerBool (&this->UndernetMsgPrefix), DT_BOOLEAN, NoValidation}, + {"options", "announceinvites", "1", new ValueContainerBool (&this->AnnounceInvites), DT_BOOLEAN, NoValidation}, + {"options", "hostintopic", "1", new ValueContainerBool (&this->FullHostInTopic), DT_BOOLEAN, NoValidation}, + {"options", "hidemodes", "", new ValueContainerChar (hidemodes), DT_CHARPTR, ValidateModeLists}, + {"options", "exemptchanops","", new ValueContainerChar (exemptchanops), DT_CHARPTR, ValidateExemptChanOps}, + {"options", "defaultmodes", "nt", new ValueContainerChar (this->DefaultModes), DT_CHARPTR, NoValidation}, + {"pid", "file", "", new ValueContainerChar (this->PID), DT_CHARPTR, NoValidation}, + {"whowas", "groupsize", "10", new ValueContainerInt (&this->WhoWasGroupSize), DT_INTEGER, NoValidation}, + {"whowas", "maxgroups", "10240", new ValueContainerInt (&this->WhoWasMaxGroups), DT_INTEGER, NoValidation}, + {"whowas", "maxkeep", "3600", new ValueContainerChar (maxkeep), DT_CHARPTR, ValidateWhoWas}, + {"die", "value", "", new ValueContainerChar (this->DieValue), DT_CHARPTR, NoValidation}, + {"channels", "users", "20", new ValueContainerUInt (&this->MaxChans), DT_INTEGER, NoValidation}, + {"channels", "opers", "60", new ValueContainerUInt (&this->OperMaxChans), DT_INTEGER, NoValidation}, + {NULL} + }; + + /* These tags can occur multiple times, and therefore they have special code to read them + * which is different to the code for reading the singular tags listed above. + */ + MultiConfig MultiValues[] = { + + {"connect", + {"allow", "deny", "password", "timeout", "pingfreq", "flood", + "threshold", "sendq", "recvq", "localmax", "globalmax", "port", + NULL}, + {"", "", "", "", "120", "", + "", "", "", "3", "3", "0", + NULL}, + {DT_CHARPTR, DT_CHARPTR, DT_CHARPTR, DT_INTEGER, DT_INTEGER, DT_INTEGER, + DT_INTEGER, DT_INTEGER, DT_INTEGER, DT_INTEGER, DT_INTEGER, DT_INTEGER}, + InitConnect, DoConnect, DoneConnect}, + + {"uline", + {"server", "silent", NULL}, + {"", "0", NULL}, + {DT_CHARPTR, DT_BOOLEAN}, + InitULine,DoULine,DoneULine}, + + {"banlist", + {"chan", "limit", NULL}, + {"", "", NULL}, + {DT_CHARPTR, DT_INTEGER}, + InitMaxBans, DoMaxBans, DoneMaxBans}, + + {"module", + {"name", NULL}, + {"", NULL}, + {DT_CHARPTR}, + InitModule, DoModule, DoneModule}, + + {"badip", + {"reason", "ipmask", NULL}, + {"No reason", "", NULL}, + {DT_CHARPTR, DT_CHARPTR}, + InitXLine, DoZLine, DoneZLine}, + + {"badnick", + {"reason", "nick", NULL}, + {"No reason", "", NULL}, + {DT_CHARPTR, DT_CHARPTR}, + InitXLine, DoQLine, DoneQLine}, + + {"badhost", + {"reason", "host", NULL}, + {"No reason", "", NULL}, + {DT_CHARPTR, DT_CHARPTR}, + InitXLine, DoKLine, DoneKLine}, + + {"exception", + {"reason", "host", NULL}, + {"No reason", "", NULL}, + {DT_CHARPTR, DT_CHARPTR}, + InitXLine, DoELine, DoneELine}, + + {"type", + {"name", "classes", NULL}, + {"", "", NULL}, + {DT_CHARPTR, DT_CHARPTR}, + InitTypes, DoType, DoneClassesAndTypes}, + + {"class", + {"name", "commands", NULL}, + {"", "", NULL}, + {DT_CHARPTR, DT_CHARPTR}, + InitClasses, DoClass, DoneClassesAndTypes}, + + {NULL} + }; + + include_stack.clear(); + + /* Load and parse the config file, if there are any errors then explode */ + + /* Make a copy here so if it fails then we can carry on running with an unaffected config */ + ConfigDataHash newconfig; + + if (this->LoadConf(newconfig, ServerInstance->ConfigFileName, errstr)) + { + /* If we succeeded, set the ircd config to the new one */ + this->config_data = newconfig; + } + else + { + ReportConfigError(errstr.str(), bail, user); + return; + } + + /* The stuff in here may throw CoreException, be sure we're in a position to catch it. */ + try + { + /* Check we dont have more than one of singular tags, or any of them missing + */ + for (int Index = 0; Once[Index]; Index++) + if (!CheckOnce(Once[Index], bail, user)) + return; + + /* Read the values of all the tags which occur once or not at all, and call their callbacks. + */ + for (int Index = 0; Values[Index].tag; Index++) + { + char item[MAXBUF]; + int dt = Values[Index].datatype; + bool allow_newlines = ((dt & DT_ALLOW_NEWLINE) > 0); + dt &= ~DT_ALLOW_NEWLINE; + + ConfValue(this->config_data, Values[Index].tag, Values[Index].value, Values[Index].default_value, 0, item, MAXBUF, allow_newlines); + ValueItem vi(item); + + if (!Values[Index].validation_function(this, Values[Index].tag, Values[Index].value, vi)) + throw CoreException("One or more values in your configuration file failed to validate. Please see your ircd.log for more information."); + + switch (Values[Index].datatype) + { + case DT_CHARPTR: + { + ValueContainerChar* vcc = (ValueContainerChar*)Values[Index].val; + /* Make sure we also copy the null terminator */ + vcc->Set(vi.GetString(), strlen(vi.GetString()) + 1); + } + break; + case DT_INTEGER: + { + int val = vi.GetInteger(); + ValueContainerInt* vci = (ValueContainerInt*)Values[Index].val; + vci->Set(&val, sizeof(int)); + } + break; + case DT_BOOLEAN: + { + bool val = vi.GetBool(); + ValueContainerBool* vcb = (ValueContainerBool*)Values[Index].val; + vcb->Set(&val, sizeof(bool)); + } + break; + default: + /* You don't want to know what happens if someones bad code sends us here. */ + break; + } + + /* We're done with this now */ + delete Values[Index].val; + } + + /* Read the multiple-tag items (class tags, connect tags, etc) + * and call the callbacks associated with them. We have three + * callbacks for these, a 'start', 'item' and 'end' callback. + */ + for (int Index = 0; MultiValues[Index].tag; Index++) + { + MultiValues[Index].init_function(this, MultiValues[Index].tag); + + int number_of_tags = ConfValueEnum(this->config_data, MultiValues[Index].tag); + + for (int tagnum = 0; tagnum < number_of_tags; tagnum++) + { + ValueList vl; + for (int valuenum = 0; MultiValues[Index].items[valuenum]; valuenum++) + { + int dt = MultiValues[Index].datatype[valuenum]; + bool allow_newlines = ((dt & DT_ALLOW_NEWLINE) > 0); + dt &= ~DT_ALLOW_NEWLINE; + + switch (dt) + { + case DT_CHARPTR: + { + char item[MAXBUF]; + if (ConfValue(this->config_data, MultiValues[Index].tag, MultiValues[Index].items[valuenum], MultiValues[Index].items_default[valuenum], tagnum, item, MAXBUF, allow_newlines)) + vl.push_back(ValueItem(item)); + else + vl.push_back(ValueItem("")); + } + break; + case DT_INTEGER: + { + int item = 0; + if (ConfValueInteger(this->config_data, MultiValues[Index].tag, MultiValues[Index].items[valuenum], MultiValues[Index].items_default[valuenum], tagnum, item)) + vl.push_back(ValueItem(item)); + else + vl.push_back(ValueItem(0)); + } + break; + case DT_BOOLEAN: + { + bool item = ConfValueBool(this->config_data, MultiValues[Index].tag, MultiValues[Index].items[valuenum], MultiValues[Index].items_default[valuenum], tagnum); + vl.push_back(ValueItem(item)); + } + break; + default: + /* Someone was smoking craq if we got here, and we're all gonna die. */ + break; + } + } + + MultiValues[Index].validation_function(this, MultiValues[Index].tag, (char**)MultiValues[Index].items, vl, MultiValues[Index].datatype); + } + + MultiValues[Index].finish_function(this, MultiValues[Index].tag); + } + + } + + catch (CoreException &ce) + { + ReportConfigError(ce.GetReason(), bail, user); + return; + } + + // write once here, to try it out and make sure its ok + ServerInstance->WritePID(this->PID); + + ServerInstance->Log(DEFAULT,"Done reading configuration file."); + + /* If we're rehashing, let's load any new modules, and unload old ones + */ + if (!bail) + { + int found_ports = 0; + FailedPortList pl; + ServerInstance->BindPorts(false, found_ports, pl); + + if (pl.size()) + { + user->WriteServ("NOTICE %s :*** Not all your client ports could be bound.", user->nick); + user->WriteServ("NOTICE %s :*** The following port(s) failed to bind:", user->nick); + int j = 1; + for (FailedPortList::iterator i = pl.begin(); i != pl.end(); i++, j++) + { + user->WriteServ("NOTICE %s :*** %d. IP: %s Port: %lu", user->nick, j, i->first.empty() ? "<all>" : i->first.c_str(), (unsigned long)i->second); + } + } + + if (!removed_modules.empty()) + { + for (std::vector<std::string>::iterator removing = removed_modules.begin(); removing != removed_modules.end(); removing++) + { + if (ServerInstance->UnloadModule(removing->c_str())) + { + ServerInstance->WriteOpers("*** REHASH UNLOADED MODULE: %s",removing->c_str()); + + if (user) + user->WriteServ("973 %s %s :Module %s successfully unloaded.",user->nick, removing->c_str(), removing->c_str()); + + rem++; + } + else + { + if (user) + user->WriteServ("972 %s %s :Failed to unload module %s: %s",user->nick, removing->c_str(), removing->c_str(), ServerInstance->ModuleError()); + } + } + } + + if (!added_modules.empty()) + { + for (std::vector<std::string>::iterator adding = added_modules.begin(); adding != added_modules.end(); adding++) + { + if (ServerInstance->LoadModule(adding->c_str())) + { + ServerInstance->WriteOpers("*** REHASH LOADED MODULE: %s",adding->c_str()); + + if (user) + user->WriteServ("975 %s %s :Module %s successfully loaded.",user->nick, adding->c_str(), adding->c_str()); + + add++; + } + else + { + if (user) + user->WriteServ("974 %s %s :Failed to load module %s: %s",user->nick, adding->c_str(), adding->c_str(), ServerInstance->ModuleError()); + } + } + } + + ServerInstance->Log(DEFAULT,"Successfully unloaded %lu of %lu modules and loaded %lu of %lu modules.",(unsigned long)rem,(unsigned long)removed_modules.size(),(unsigned long)add,(unsigned long)added_modules.size()); + } + + if (user) + user->WriteServ("NOTICE %s :*** Successfully rehashed server.", user->nick); + else + ServerInstance->WriteOpers("*** Successfully rehashed server."); +} + +bool ServerConfig::LoadConf(ConfigDataHash &target, const char* filename, std::ostringstream &errorstream) +{ + std::ifstream conf(filename); + std::string line; + char ch; + long linenumber; + bool in_tag; + bool in_quote; + bool in_comment; + int character_count = 0; + + linenumber = 1; + in_tag = false; + in_quote = false; + in_comment = false; + + /* Check if the file open failed first */ + if (!conf) + { + errorstream << "LoadConf: Couldn't open config file: " << filename << std::endl; + return false; + } + + /* Fix the chmod of the file to restrict it to the current user and group */ + chmod(filename,0600); + + for (unsigned int t = 0; t < include_stack.size(); t++) + { + if (std::string(filename) == include_stack[t]) + { + errorstream << "File " << filename << " is included recursively (looped inclusion)." << std::endl; + return false; + } + } + + /* It's not already included, add it to the list of files we've loaded */ + include_stack.push_back(filename); + + /* Start reading characters... */ + while(conf.get(ch)) + { + + /* + * Fix for moronic windows issue spotted by Adremelech. + * Some windows editors save text files as utf-16, which is + * a total pain in the ass to parse. Users should save in the + * right config format! If we ever see a file where the first + * byte is 0xFF or 0xFE, or the second is 0xFF or 0xFE, then + * this is most likely a utf-16 file. Bail out and insult user. + */ + if ((character_count++ < 2) && (ch == '\xFF' || ch == '\xFE')) + { + errorstream << "File " << filename << " cannot be read, as it is encoded in braindead UTF-16. Save your file as plain ASCII!" << std::endl; + return false; + } + + /* + * Here we try and get individual tags on separate lines, + * this would be so easy if we just made people format + * their config files like that, but they don't so... + * We check for a '<' and then know the line is over when + * we get a '>' not inside quotes. If we find two '<' and + * no '>' then die with an error. + */ + + if((ch == '#') && !in_quote) + in_comment = true; + + switch(ch) + { + case '\n': + if (in_quote) + line += '\n'; + linenumber++; + case '\r': + if (!in_quote) + in_comment = false; + case '\0': + continue; + case '\t': + ch = ' '; + } + + if(in_comment) + continue; + + /* XXX: Added by Brain, May 1st 2006 - Escaping of characters. + * Note that this WILL NOT usually allow insertion of newlines, + * because a newline is two characters long. Use it primarily to + * insert the " symbol. + * + * Note that this also involves a further check when parsing the line, + * which can be found below. + */ + if ((ch == '\\') && (in_quote) && (in_tag)) + { + line += ch; + char real_character; + if (conf.get(real_character)) + { + if (real_character == 'n') + real_character = '\n'; + line += real_character; + continue; + } + else + { + errorstream << "End of file after a \\, what did you want to escape?: " << filename << ":" << linenumber << std::endl; + return false; + } + } + + if (ch != '\r') + line += ch; + + if(ch == '<') + { + if(in_tag) + { + if(!in_quote) + { + errorstream << "Got another opening < when the first one wasn't closed: " << filename << ":" << linenumber << std::endl; + return false; + } + } + else + { + if(in_quote) + { + errorstream << "We're in a quote but outside a tag, interesting. " << filename << ":" << linenumber << std::endl; + return false; + } + else + { + // errorstream << "Opening new config tag on line " << linenumber << std::endl; + in_tag = true; + } + } + } + else if(ch == '"') + { + if(in_tag) + { + if(in_quote) + { + // errorstream << "Closing quote in config tag on line " << linenumber << std::endl; + in_quote = false; + } + else + { + // errorstream << "Opening quote in config tag on line " << linenumber << std::endl; + in_quote = true; + } + } + else + { + if(in_quote) + { + errorstream << "Found a (closing) \" outside a tag: " << filename << ":" << linenumber << std::endl; + } + else + { + errorstream << "Found a (opening) \" outside a tag: " << filename << ":" << linenumber << std::endl; + } + } + } + else if(ch == '>') + { + if(!in_quote) + { + if(in_tag) + { + // errorstream << "Closing config tag on line " << linenumber << std::endl; + in_tag = false; + + /* + * If this finds an <include> then ParseLine can simply call + * LoadConf() and load the included config into the same ConfigDataHash + */ + + if(!this->ParseLine(target, line, linenumber, errorstream)) + return false; + + line.clear(); + } + else + { + errorstream << "Got a closing > when we weren't inside a tag: " << filename << ":" << linenumber << std::endl; + return false; + } + } + } + } + + return true; +} + +bool ServerConfig::LoadConf(ConfigDataHash &target, const std::string &filename, std::ostringstream &errorstream) +{ + return this->LoadConf(target, filename.c_str(), errorstream); +} + +bool ServerConfig::ParseLine(ConfigDataHash &target, std::string &line, long linenumber, std::ostringstream &errorstream) +{ + std::string tagname; + std::string current_key; + std::string current_value; + KeyValList results; + bool got_name; + bool got_key; + bool in_quote; + + got_name = got_key = in_quote = false; + + //std::cout << "ParseLine(data, '" << line << "', " << linenumber << ", stream)" << std::endl; + + for(std::string::iterator c = line.begin(); c != line.end(); c++) + { + if(!got_name) + { + /* We don't know the tag name yet. */ + + if(*c != ' ') + { + if(*c != '<') + { + tagname += *c; + } + } + else + { + /* We got to a space, we should have the tagname now. */ + if(tagname.length()) + { + got_name = true; + } + } + } + else + { + /* We have the tag name */ + if (!got_key) + { + /* We're still reading the key name */ + if (*c != '=') + { + if (*c != ' ') + { + current_key += *c; + } + } + else + { + /* We got an '=', end of the key name. */ + got_key = true; + } + } + else + { + /* We have the key name, now we're looking for quotes and the value */ + + /* Correctly handle escaped characters here. + * See the XXX'ed section above. + */ + if ((*c == '\\') && (in_quote)) + { + c++; + if (*c == 'n') + current_value += '\n'; + else + current_value += *c; + continue; + } + else if ((*c == '\n') && (in_quote)) + { + /* Got a 'real' \n, treat it as part of the value */ + current_value += '\n'; + continue; + } + else if ((*c == '\r') && (in_quote)) + /* Got a \r, drop it */ + continue; + + if (*c == '"') + { + if (!in_quote) + { + /* We're not already in a quote. */ + in_quote = true; + } + else + { + /* Leaving quotes, we have the value */ + results.push_back(KeyVal(current_key, current_value)); + + // std::cout << "<" << tagname << ":" << current_key << "> " << current_value << std::endl; + + in_quote = false; + got_key = false; + + if((tagname == "include") && (current_key == "file")) + { + if(!this->DoInclude(target, current_value, errorstream)) + return false; + } + + current_key.clear(); + current_value.clear(); + } + } + else + { + if(in_quote) + { + current_value += *c; + } + } + } + } + } + + /* Finished parsing the tag, add it to the config hash */ + target.insert(std::pair<std::string, KeyValList > (tagname, results)); + + return true; +} + +bool ServerConfig::DoInclude(ConfigDataHash &target, const std::string &file, std::ostringstream &errorstream) +{ + std::string confpath; + std::string newfile; + std::string::size_type pos; + + confpath = ServerInstance->ConfigFileName; + newfile = file; + + for (std::string::iterator c = newfile.begin(); c != newfile.end(); c++) + { + if (*c == '\\') + { + *c = '/'; + } + } + + if (file[0] != '/') + { + if((pos = confpath.rfind("/")) != std::string::npos) + { + /* Leaves us with just the path */ + newfile = confpath.substr(0, pos) + std::string("/") + newfile; + } + else + { + errorstream << "Couldn't get config path from: " << confpath << std::endl; + return false; + } + } + + return LoadConf(target, newfile, errorstream); +} + +bool ServerConfig::ConfValue(ConfigDataHash &target, const char* tag, const char* var, int index, char* result, int length, bool allow_linefeeds) +{ + return ConfValue(target, tag, var, "", index, result, length, allow_linefeeds); +} + +bool ServerConfig::ConfValue(ConfigDataHash &target, const char* tag, const char* var, const char* default_value, int index, char* result, int length, bool allow_linefeeds) +{ + std::string value; + bool r = ConfValue(target, std::string(tag), std::string(var), std::string(default_value), index, value, allow_linefeeds); + strlcpy(result, value.c_str(), length); + return r; +} + +bool ServerConfig::ConfValue(ConfigDataHash &target, const std::string &tag, const std::string &var, int index, std::string &result, bool allow_linefeeds) +{ + return ConfValue(target, tag, var, "", index, result, allow_linefeeds); +} + +bool ServerConfig::ConfValue(ConfigDataHash &target, const std::string &tag, const std::string &var, const std::string &default_value, int index, std::string &result, bool allow_linefeeds) +{ + ConfigDataHash::size_type pos = index; + if((pos >= 0) && (pos < target.count(tag))) + { + ConfigDataHash::iterator iter = target.find(tag); + + for(int i = 0; i < index; i++) + iter++; + + for(KeyValList::iterator j = iter->second.begin(); j != iter->second.end(); j++) + { + if(j->first == var) + { + if ((!allow_linefeeds) && (j->second.find('\n') != std::string::npos)) + { + ServerInstance->Log(DEFAULT, "Value of <" + tag + ":" + var+ "> contains a linefeed, and linefeeds in this value are not permitted -- stripped to spaces."); + for (std::string::iterator n = j->second.begin(); n != j->second.end(); n++) + if (*n == '\n') + *n = ' '; + } + else + { + result = j->second; + return true; + } + } + } + if (!default_value.empty()) + { + result = default_value; + return true; + } + } + else if(pos == 0) + { + if (!default_value.empty()) + { + result = default_value; + return true; + } + } + return false; +} + +bool ServerConfig::ConfValueInteger(ConfigDataHash &target, const char* tag, const char* var, int index, int &result) +{ + return ConfValueInteger(target, std::string(tag), std::string(var), "", index, result); +} + +bool ServerConfig::ConfValueInteger(ConfigDataHash &target, const char* tag, const char* var, const char* default_value, int index, int &result) +{ + return ConfValueInteger(target, std::string(tag), std::string(var), std::string(default_value), index, result); +} + +bool ServerConfig::ConfValueInteger(ConfigDataHash &target, const std::string &tag, const std::string &var, int index, int &result) +{ + return ConfValueInteger(target, tag, var, "", index, result); +} + +bool ServerConfig::ConfValueInteger(ConfigDataHash &target, const std::string &tag, const std::string &var, const std::string &default_value, int index, int &result) +{ + std::string value; + std::istringstream stream; + bool r = ConfValue(target, tag, var, default_value, index, value); + stream.str(value); + if(!(stream >> result)) + return false; + else + { + if (!value.empty()) + { + if (value.substr(0,2) == "0x") + { + char* endptr; + + value.erase(0,2); + result = strtol(value.c_str(), &endptr, 16); + + /* No digits found */ + if (endptr == value.c_str()) + return false; + } + else + { + char denominator = *(value.end() - 1); + switch (toupper(denominator)) + { + case 'K': + /* Kilobytes -> bytes */ + result = result * 1024; + break; + case 'M': + /* Megabytes -> bytes */ + result = result * 1024 * 1024; + break; + case 'G': + /* Gigabytes -> bytes */ + result = result * 1024 * 1024 * 1024; + break; + } + } + } + } + return r; +} + + +bool ServerConfig::ConfValueBool(ConfigDataHash &target, const char* tag, const char* var, int index) +{ + return ConfValueBool(target, std::string(tag), std::string(var), "", index); +} + +bool ServerConfig::ConfValueBool(ConfigDataHash &target, const char* tag, const char* var, const char* default_value, int index) +{ + return ConfValueBool(target, std::string(tag), std::string(var), std::string(default_value), index); +} + +bool ServerConfig::ConfValueBool(ConfigDataHash &target, const std::string &tag, const std::string &var, int index) +{ + return ConfValueBool(target, tag, var, "", index); +} + +bool ServerConfig::ConfValueBool(ConfigDataHash &target, const std::string &tag, const std::string &var, const std::string &default_value, int index) +{ + std::string result; + if(!ConfValue(target, tag, var, default_value, index, result)) + return false; + + return ((result == "yes") || (result == "true") || (result == "1")); +} + +int ServerConfig::ConfValueEnum(ConfigDataHash &target, const char* tag) +{ + return target.count(tag); +} + +int ServerConfig::ConfValueEnum(ConfigDataHash &target, const std::string &tag) +{ + return target.count(tag); +} + +int ServerConfig::ConfVarEnum(ConfigDataHash &target, const char* tag, int index) +{ + return ConfVarEnum(target, std::string(tag), index); +} + +int ServerConfig::ConfVarEnum(ConfigDataHash &target, const std::string &tag, int index) +{ + ConfigDataHash::size_type pos = index; + + if((pos >= 0) && (pos < target.count(tag))) + { + ConfigDataHash::const_iterator iter = target.find(tag); + + for(int i = 0; i < index; i++) + iter++; + + return iter->second.size(); + } + + return 0; +} + +/** Read the contents of a file located by `fname' into a file_cache pointed at by `F'. + */ +bool ServerConfig::ReadFile(file_cache &F, const char* fname) +{ + if (!fname || !*fname) + return false; + + FILE* file = NULL; + char linebuf[MAXBUF]; + + F.clear(); + + if ((*fname != '/') && (*fname != '\\')) + { + std::string::size_type pos; + std::string confpath = ServerInstance->ConfigFileName; + std::string newfile = fname; + + if ((pos = confpath.rfind("/")) != std::string::npos) + newfile = confpath.substr(0, pos) + std::string("/") + fname; + else if ((pos = confpath.rfind("\\")) != std::string::npos) + newfile = confpath.substr(0, pos) + std::string("\\") + fname; + + if (!FileExists(newfile.c_str())) + return false; + file = fopen(newfile.c_str(), "r"); + } + else + { + if (!FileExists(fname)) + return false; + file = fopen(fname, "r"); + } + + if (file) + { + while (!feof(file)) + { + if (fgets(linebuf, sizeof(linebuf), file)) + linebuf[strlen(linebuf)-1] = 0; + else + *linebuf = 0; + + if (!feof(file)) + { + F.push_back(*linebuf ? linebuf : " "); + } + } + + fclose(file); + } + else + return false; + + return true; +} + +bool ServerConfig::FileExists(const char* file) +{ + struct stat sb; + if (stat(file, &sb) == -1) + return false; + + if ((sb.st_mode & S_IFDIR) > 0) + return false; + + FILE *input; + if ((input = fopen (file, "r")) == NULL) + return false; + else + { + fclose(input); + return true; + } +} + +char* ServerConfig::CleanFilename(char* name) +{ + char* p = name + strlen(name); + while ((p != name) && (*p != '/') && (*p != '\\')) p--; + return (p != name ? ++p : p); +} + + +bool ServerConfig::DirValid(const char* dirandfile) +{ +#ifdef WINDOWS + return true; +#endif + + char work[1024]; + char buffer[1024]; + char otherdir[1024]; + int p; + + strlcpy(work, dirandfile, 1024); + p = strlen(work); + + // we just want the dir + while (*work) + { + if (work[p] == '/') + { + work[p] = '\0'; + break; + } + + work[p--] = '\0'; + } + + // Get the current working directory + if (getcwd(buffer, 1024 ) == NULL ) + return false; + + if (chdir(work) == -1) + return false; + + if (getcwd(otherdir, 1024 ) == NULL ) + return false; + + if (chdir(buffer) == -1) + return false; + + size_t t = strlen(work); + + if (strlen(otherdir) >= t) + { + otherdir[t] = '\0'; + if (!strcmp(otherdir,work)) + { + return true; + } + + return false; + } + else + { + return false; + } +} + +std::string ServerConfig::GetFullProgDir() +{ + char buffer[PATH_MAX+1]; +#ifdef WINDOWS + /* Windows has specific api calls to get the exe path that never fail. + * For once, windows has something of use, compared to the POSIX code + * for this, this is positively neato. + */ + if (GetModuleFileName(NULL, buffer, MAX_PATH)) + { + std::string fullpath = buffer; + std::string::size_type n = fullpath.rfind("\\inspircd.exe"); + return std::string(fullpath, 0, n); + } +#else + // Get the current working directory + if (getcwd(buffer, PATH_MAX)) + { + std::string remainder = this->argv[0]; + + /* Does argv[0] start with /? its a full path, use it */ + if (remainder[0] == '/') + { + std::string::size_type n = remainder.rfind("/inspircd"); + return std::string(remainder, 0, n); + } + + std::string fullpath = std::string(buffer) + "/" + remainder; + std::string::size_type n = fullpath.rfind("/inspircd"); + return std::string(fullpath, 0, n); + } +#endif + return "/"; +} + +InspIRCd* ServerConfig::GetInstance() +{ + return ServerInstance; +} + + +ValueItem::ValueItem(int value) +{ + std::stringstream n; + n << value; + v = n.str(); +} + +ValueItem::ValueItem(bool value) +{ + std::stringstream n; + n << value; + v = n.str(); +} + +ValueItem::ValueItem(char* value) +{ + v = value; +} + +void ValueItem::Set(char* value) +{ + v = value; +} + +void ValueItem::Set(const char* value) +{ + v = value; +} + +void ValueItem::Set(int value) +{ + std::stringstream n; + n << value; + v = n.str(); +} + +int ValueItem::GetInteger() +{ + if (v.empty()) + return 0; + return atoi(v.c_str()); +} + +char* ValueItem::GetString() +{ + return (char*)v.c_str(); +} + +bool ValueItem::GetBool() +{ + return (GetInteger() || v == "yes" || v == "true"); +} + diff --git a/src/cull_list.cpp b/src/cull_list.cpp index 049d2e6ba..ee257350e 100644 --- a/src/cull_list.cpp +++ b/src/cull_list.cpp @@ -1 +1,202 @@ -/* +------------------------------------+
* | 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 "users.h"
#include "cull_list.h"
CullItem::CullItem(userrec* u, std::string &r, const char* o_reason)
{
this->user = u;
this->reason = r;
this->silent = false;
/* Seperate oper reason not set, use the user reason */
if (*o_reason)
this->oper_reason = o_reason;
else
this->oper_reason = r;
}
CullItem::CullItem(userrec* u, const char* r, const char* o_reason)
{
this->user = u;
this->reason = r;
this->silent = false;
/* Seperate oper reason not set, use the user reason */
if (*o_reason)
this->oper_reason = o_reason;
else
this->oper_reason = r;
}
void CullItem::MakeSilent()
{
this->silent = true;
}
bool CullItem::IsSilent()
{
return this->silent;
}
CullItem::~CullItem()
{
}
userrec* CullItem::GetUser()
{
return this->user;
}
std::string& CullItem::GetReason()
{
return this->reason;
}
std::string& CullItem::GetOperReason()
{
return this->oper_reason;
}
CullList::CullList(InspIRCd* Instance) : ServerInstance(Instance)
{
list.clear();
exempt.clear();
}
void CullList::AddItem(userrec* user, std::string &reason, const char* o_reason)
{
AddItem(user, reason.c_str(), o_reason);
}
void CullList::AddItem(userrec* user, const char* reason, const char* o_reason)
{
if (exempt.find(user) == exempt.end())
{
CullItem item(user, reason, o_reason);
list.push_back(item);
exempt[user] = user;
}
}
void CullList::MakeSilent(userrec* user)
{
for (std::vector<CullItem>::iterator a = list.begin(); a != list.end(); ++a)
{
if (a->GetUser() == user)
{
a->MakeSilent();
break;
}
}
return;
}
int CullList::Apply()
{
int n = list.size();
while (list.size())
{
std::vector<CullItem>::iterator a = list.begin();
user_hash::iterator iter = ServerInstance->clientlist->find(a->GetUser()->nick);
std::map<userrec*, userrec*>::iterator exemptiter = exempt.find(a->GetUser());
const char* preset_reason = a->GetUser()->GetOperQuit();
std::string reason = a->GetReason();
std::string oper_reason = *preset_reason ? preset_reason : a->GetOperReason();
if (reason.length() > MAXQUIT - 1)
reason.resize(MAXQUIT - 1);
if (oper_reason.length() > MAXQUIT - 1)
oper_reason.resize(MAXQUIT - 1);
if (a->GetUser()->registered != REG_ALL)
if (ServerInstance->unregistered_count)
ServerInstance->unregistered_count--;
if (IS_LOCAL(a->GetUser()))
{
a->GetUser()->Write("ERROR :Closing link (%s@%s) [%s]", a->GetUser()->ident, a->GetUser()->host, oper_reason.c_str());
if ((!a->GetUser()->sendq.empty()) && (!(*a->GetUser()->GetWriteError())))
a->GetUser()->FlushWriteBuf();
}
if (a->GetUser()->registered == REG_ALL)
{
FOREACH_MOD_I(ServerInstance,I_OnUserQuit,OnUserQuit(a->GetUser(), reason, oper_reason));
a->GetUser()->PurgeEmptyChannels();
a->GetUser()->WriteCommonQuit(reason, oper_reason);
}
FOREACH_MOD_I(ServerInstance,I_OnUserDisconnect,OnUserDisconnect(a->GetUser()));
if (IS_LOCAL(a->GetUser()))
{
if (ServerInstance->Config->GetIOHook(a->GetUser()->GetPort()))
{
try
{
ServerInstance->Config->GetIOHook(a->GetUser()->GetPort())->OnRawSocketClose(a->GetUser()->GetFd());
}
catch (CoreException& modexcept)
{
ServerInstance->Log(DEBUG, "%s threw an exception: %s", modexcept.GetSource(), modexcept.GetReason());
}
}
ServerInstance->SE->DelFd(a->GetUser());
a->GetUser()->CloseSocket();
}
/*
* this must come before the ServerInstance->SNO->WriteToSnoMaskso that it doesnt try to fill their buffer with anything
* if they were an oper with +sn +qQ.
*/
if (a->GetUser()->registered == REG_ALL)
{
if (IS_LOCAL(a->GetUser()))
{
if (!a->IsSilent())
{
ServerInstance->SNO->WriteToSnoMask('q',"Client exiting: %s!%s@%s [%s]",a->GetUser()->nick,a->GetUser()->ident,a->GetUser()->host,oper_reason.c_str());
}
}
else
{
if ((!ServerInstance->SilentULine(a->GetUser()->server)) && (!a->IsSilent()))
{
ServerInstance->SNO->WriteToSnoMask('Q',"Client exiting on server %s: %s!%s@%s [%s]",a->GetUser()->server,a->GetUser()->nick,a->GetUser()->ident,a->GetUser()->host,oper_reason.c_str());
}
}
a->GetUser()->AddToWhoWas();
}
if (iter != ServerInstance->clientlist->end())
{
if (IS_LOCAL(a->GetUser()))
{
std::vector<userrec*>::iterator x = find(ServerInstance->local_users.begin(),ServerInstance->local_users.end(),a->GetUser());
if (x != ServerInstance->local_users.end())
ServerInstance->local_users.erase(x);
}
ServerInstance->clientlist->erase(iter);
DELETE(a->GetUser());
}
list.erase(list.begin());
exempt.erase(exemptiter);
}
return n;
}
\ No newline at end of file +/* +------------------------------------+ + * | 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 "users.h" +#include "cull_list.h" + +CullItem::CullItem(userrec* u, std::string &r, const char* o_reason) +{ + this->user = u; + this->reason = r; + this->silent = false; + /* Seperate oper reason not set, use the user reason */ + if (*o_reason) + this->oper_reason = o_reason; + else + this->oper_reason = r; +} + +CullItem::CullItem(userrec* u, const char* r, const char* o_reason) +{ + this->user = u; + this->reason = r; + this->silent = false; + /* Seperate oper reason not set, use the user reason */ + if (*o_reason) + this->oper_reason = o_reason; + else + this->oper_reason = r; +} + +void CullItem::MakeSilent() +{ + this->silent = true; +} + +bool CullItem::IsSilent() +{ + return this->silent; +} + +CullItem::~CullItem() +{ +} + +userrec* CullItem::GetUser() +{ + return this->user; +} + +std::string& CullItem::GetReason() +{ + return this->reason; +} + +std::string& CullItem::GetOperReason() +{ + return this->oper_reason; +} + +CullList::CullList(InspIRCd* Instance) : ServerInstance(Instance) +{ + list.clear(); + exempt.clear(); +} + +void CullList::AddItem(userrec* user, std::string &reason, const char* o_reason) +{ + AddItem(user, reason.c_str(), o_reason); +} + + +void CullList::AddItem(userrec* user, const char* reason, const char* o_reason) +{ + if (exempt.find(user) == exempt.end()) + { + CullItem item(user, reason, o_reason); + list.push_back(item); + exempt[user] = user; + } +} + +void CullList::MakeSilent(userrec* user) +{ + for (std::vector<CullItem>::iterator a = list.begin(); a != list.end(); ++a) + { + if (a->GetUser() == user) + { + a->MakeSilent(); + break; + } + } + return; +} + +int CullList::Apply() +{ + int n = list.size(); + while (list.size()) + { + std::vector<CullItem>::iterator a = list.begin(); + + user_hash::iterator iter = ServerInstance->clientlist->find(a->GetUser()->nick); + std::map<userrec*, userrec*>::iterator exemptiter = exempt.find(a->GetUser()); + const char* preset_reason = a->GetUser()->GetOperQuit(); + std::string reason = a->GetReason(); + std::string oper_reason = *preset_reason ? preset_reason : a->GetOperReason(); + + if (reason.length() > MAXQUIT - 1) + reason.resize(MAXQUIT - 1); + if (oper_reason.length() > MAXQUIT - 1) + oper_reason.resize(MAXQUIT - 1); + + if (a->GetUser()->registered != REG_ALL) + if (ServerInstance->unregistered_count) + ServerInstance->unregistered_count--; + + if (IS_LOCAL(a->GetUser())) + { + a->GetUser()->Write("ERROR :Closing link (%s@%s) [%s]", a->GetUser()->ident, a->GetUser()->host, oper_reason.c_str()); + if ((!a->GetUser()->sendq.empty()) && (!(*a->GetUser()->GetWriteError()))) + a->GetUser()->FlushWriteBuf(); + } + + if (a->GetUser()->registered == REG_ALL) + { + FOREACH_MOD_I(ServerInstance,I_OnUserQuit,OnUserQuit(a->GetUser(), reason, oper_reason)); + a->GetUser()->PurgeEmptyChannels(); + a->GetUser()->WriteCommonQuit(reason, oper_reason); + } + + FOREACH_MOD_I(ServerInstance,I_OnUserDisconnect,OnUserDisconnect(a->GetUser())); + + if (IS_LOCAL(a->GetUser())) + { + if (ServerInstance->Config->GetIOHook(a->GetUser()->GetPort())) + { + try + { + ServerInstance->Config->GetIOHook(a->GetUser()->GetPort())->OnRawSocketClose(a->GetUser()->GetFd()); + } + catch (CoreException& modexcept) + { + ServerInstance->Log(DEBUG, "%s threw an exception: %s", modexcept.GetSource(), modexcept.GetReason()); + } + } + + ServerInstance->SE->DelFd(a->GetUser()); + a->GetUser()->CloseSocket(); + } + + /* + * this must come before the ServerInstance->SNO->WriteToSnoMaskso that it doesnt try to fill their buffer with anything + * if they were an oper with +sn +qQ. + */ + if (a->GetUser()->registered == REG_ALL) + { + if (IS_LOCAL(a->GetUser())) + { + if (!a->IsSilent()) + { + ServerInstance->SNO->WriteToSnoMask('q',"Client exiting: %s!%s@%s [%s]",a->GetUser()->nick,a->GetUser()->ident,a->GetUser()->host,oper_reason.c_str()); + } + } + else + { + if ((!ServerInstance->SilentULine(a->GetUser()->server)) && (!a->IsSilent())) + { + ServerInstance->SNO->WriteToSnoMask('Q',"Client exiting on server %s: %s!%s@%s [%s]",a->GetUser()->server,a->GetUser()->nick,a->GetUser()->ident,a->GetUser()->host,oper_reason.c_str()); + } + } + a->GetUser()->AddToWhoWas(); + } + + if (iter != ServerInstance->clientlist->end()) + { + if (IS_LOCAL(a->GetUser())) + { + std::vector<userrec*>::iterator x = find(ServerInstance->local_users.begin(),ServerInstance->local_users.end(),a->GetUser()); + if (x != ServerInstance->local_users.end()) + ServerInstance->local_users.erase(x); + } + ServerInstance->clientlist->erase(iter); + DELETE(a->GetUser()); + } + + list.erase(list.begin()); + exempt.erase(exemptiter); + } + return n; +} + diff --git a/src/dns.cpp b/src/dns.cpp index 6e12662a7..fab9631b7 100644 --- a/src/dns.cpp +++ b/src/dns.cpp @@ -1 +1,1169 @@ -/* +------------------------------------+
* | 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.
*
* ---------------------------------------------------
*/
/*
dns.cpp - Nonblocking DNS functions.
Very very loosely based on the firedns library,
Copyright (C) 2002 Ian Gulliver. This file is no
longer anything like firedns, there are many major
differences between this code and the original.
Please do not assume that firedns works like this,
looks like this, walks like this or tastes like this.
*/
#ifndef WIN32
#include <sys/types.h>
#include <sys/socket.h>
#include <errno.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#else
#include "inspircd_win32wrapper.h"
#include "inspircd_se_config.h"
#endif
#include "dns.h"
#include "inspircd.h"
#include "socketengine.h"
#include "configreader.h"
#include "socket.h"
using irc::sockets::insp_inaddr;
using irc::sockets::insp_ntoa;
using irc::sockets::insp_aton;
using irc::sockets::OpenTCPSocket;
/** Masks to mask off the responses we get from the DNSRequest methods
*/
enum QueryInfo
{
ERROR_MASK = 0x10000 /* Result is an error */
};
/** Flags which can be ORed into a request or reply for different meanings
*/
enum QueryFlags
{
FLAGS_MASK_RD = 0x01, /* Recursive */
FLAGS_MASK_TC = 0x02,
FLAGS_MASK_AA = 0x04, /* Authoritative */
FLAGS_MASK_OPCODE = 0x78,
FLAGS_MASK_QR = 0x80,
FLAGS_MASK_RCODE = 0x0F, /* Request */
FLAGS_MASK_Z = 0x70,
FLAGS_MASK_RA = 0x80
};
/** Represents a dns resource record (rr)
*/
struct ResourceRecord
{
QueryType type; /* Record type */
unsigned int rr_class; /* Record class */
unsigned long ttl; /* Time to live */
unsigned int rdlength; /* Record length */
};
/** Represents a dns request/reply header, and its payload as opaque data.
*/
class DNSHeader
{
public:
unsigned char id[2]; /* Request id */
unsigned int flags1; /* Flags */
unsigned int flags2; /* Flags */
unsigned int qdcount;
unsigned int ancount; /* Answer count */
unsigned int nscount; /* Nameserver count */
unsigned int arcount;
unsigned char payload[512]; /* Packet payload */
};
class DNSRequest
{
public:
unsigned char id[2]; /* Request id */
unsigned char* res; /* Result processing buffer */
unsigned int rr_class; /* Request class */
QueryType type; /* Request type */
DNS* dnsobj; /* DNS caller (where we get our FD from) */
unsigned long ttl; /* Time to live */
std::string orig; /* Original requested name/ip */
DNSRequest(InspIRCd* Instance, DNS* dns, int id, const std::string &original);
~DNSRequest();
DNSInfo ResultIsReady(DNSHeader &h, int length);
int SendRequests(const DNSHeader *header, const int length, QueryType qt);
};
class CacheTimer : public InspTimer
{
private:
InspIRCd* ServerInstance;
DNS* dns;
public:
CacheTimer(InspIRCd* Instance, DNS* thisdns)
: InspTimer(3600, Instance->Time(), true), ServerInstance(Instance), dns(thisdns) { }
virtual void Tick(time_t TIME)
{
dns->PruneCache();
}
};
class RequestTimeout : public InspTimer
{
InspIRCd* ServerInstance;
DNSRequest* watch;
int watchid;
public:
RequestTimeout(unsigned long n, InspIRCd* SI, DNSRequest* watching, int id) : InspTimer(n, time(NULL)), ServerInstance(SI), watch(watching), watchid(id)
{
}
void Tick(time_t TIME)
{
if (ServerInstance->Res->requests[watchid] == watch)
{
/* Still exists, whack it */
if (ServerInstance->Res->Classes[watchid])
{
ServerInstance->Res->Classes[watchid]->OnError(RESOLVER_TIMEOUT, "Request timed out");
delete ServerInstance->Res->Classes[watchid];
ServerInstance->Res->Classes[watchid] = NULL;
}
ServerInstance->Res->requests[watchid] = NULL;
DELETE(watch);
return;
}
}
};
/* Allocate the processing buffer */
DNSRequest::DNSRequest(InspIRCd* Instance, DNS* dns, int id, const std::string &original) : dnsobj(dns)
{
res = new unsigned char[512];
*res = 0;
orig = original;
RequestTimeout* RT = new RequestTimeout(Instance->Config->dns_timeout ? Instance->Config->dns_timeout : 5, Instance, this, id);
Instance->Timers->AddTimer(RT); /* The timer manager frees this */
}
/* Deallocate the processing buffer */
DNSRequest::~DNSRequest()
{
delete[] res;
}
/** Fill a ResourceRecord class based on raw data input */
inline void DNS::FillResourceRecord(ResourceRecord* rr, const unsigned char *input)
{
rr->type = (QueryType)((input[0] << 8) + input[1]);
rr->rr_class = (input[2] << 8) + input[3];
rr->ttl = (input[4] << 24) + (input[5] << 16) + (input[6] << 8) + input[7];
rr->rdlength = (input[8] << 8) + input[9];
}
/** Fill a DNSHeader class based on raw data input of a given length */
inline void DNS::FillHeader(DNSHeader *header, const unsigned char *input, const int length)
{
header->id[0] = input[0];
header->id[1] = input[1];
header->flags1 = input[2];
header->flags2 = input[3];
header->qdcount = (input[4] << 8) + input[5];
header->ancount = (input[6] << 8) + input[7];
header->nscount = (input[8] << 8) + input[9];
header->arcount = (input[10] << 8) + input[11];
memcpy(header->payload,&input[12],length);
}
/** Empty a DNSHeader class out into raw data, ready for transmission */
inline void DNS::EmptyHeader(unsigned char *output, const DNSHeader *header, const int length)
{
output[0] = header->id[0];
output[1] = header->id[1];
output[2] = header->flags1;
output[3] = header->flags2;
output[4] = header->qdcount >> 8;
output[5] = header->qdcount & 0xFF;
output[6] = header->ancount >> 8;
output[7] = header->ancount & 0xFF;
output[8] = header->nscount >> 8;
output[9] = header->nscount & 0xFF;
output[10] = header->arcount >> 8;
output[11] = header->arcount & 0xFF;
memcpy(&output[12],header->payload,length);
}
/** Send requests we have previously built down the UDP socket */
int DNSRequest::SendRequests(const DNSHeader *header, const int length, QueryType qt)
{
unsigned char payload[sizeof(DNSHeader)];
this->rr_class = 1;
this->type = qt;
DNS::EmptyHeader(payload,header,length);
#ifdef IPV6
if (this->dnsobj->socketfamily == AF_INET6)
{
sockaddr_in6 addr;
memset(&addr,0,sizeof(addr));
memcpy(&addr.sin6_addr,&dnsobj->myserver6,sizeof(addr.sin6_addr));
addr.sin6_family = AF_INET6;
addr.sin6_port = htons(DNS::QUERY_PORT);
if (sendto(dnsobj->GetFd(), payload, length + 12, 0, (sockaddr *) &addr, sizeof(addr)) != length+12)
return -1;
}
else
{
sockaddr_in addr;
memset(&addr,0,sizeof(addr));
memcpy(&addr.sin_addr.s_addr,&dnsobj->myserver4,sizeof(addr.sin_addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(DNS::QUERY_PORT);
if (sendto(dnsobj->GetFd(), payload, length + 12, 0, (sockaddr *) &addr, sizeof(addr)) != length+12)
return -1;
}
#else
sockaddr_in addr;
memset(&addr,0,sizeof(addr));
memcpy(&addr.sin_addr.s_addr, &dnsobj->myserver4, sizeof(addr.sin_addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(DNS::QUERY_PORT);
if (sendto(dnsobj->GetFd(), (const char*)payload, length + 12, 0, (sockaddr *) &addr, sizeof(addr)) != length+12)
return -1;
#endif
return 0;
}
/** Add a query with a predefined header, and allocate an ID for it. */
DNSRequest* DNS::AddQuery(DNSHeader *header, int &id, const char* original)
{
/* Is the DNS connection down? */
if (this->GetFd() == -1)
return NULL;
/* Create an id */
id = this->PRNG() & DNS::MAX_REQUEST_ID;
/* If this id is already 'in flight', pick another. */
while (requests[id])
id = this->PRNG() & DNS::MAX_REQUEST_ID;
DNSRequest* req = new DNSRequest(ServerInstance, this, id, original);
header->id[0] = req->id[0] = id >> 8;
header->id[1] = req->id[1] = id & 0xFF;
header->flags1 = FLAGS_MASK_RD;
header->flags2 = 0;
header->qdcount = 1;
header->ancount = 0;
header->nscount = 0;
header->arcount = 0;
/* At this point we already know the id doesnt exist,
* so there needs to be no second check for the ::end()
*/
requests[id] = req;
/* According to the C++ spec, new never returns NULL. */
return req;
}
int DNS::ClearCache()
{
/* This ensures the buckets are reset to sane levels */
int rv = this->cache->size();
delete this->cache;
this->cache = new dnscache();
return rv;
}
int DNS::PruneCache()
{
int n = 0;
dnscache* newcache = new dnscache();
for (dnscache::iterator i = this->cache->begin(); i != this->cache->end(); i++)
/* Dont include expired items (theres no point) */
if (i->second.CalcTTLRemaining())
newcache->insert(*i);
else
n++;
delete this->cache;
this->cache = newcache;
return n;
}
void DNS::Rehash()
{
ip6munge = false;
int portpass = 0;
if (this->GetFd() > -1)
{
if (ServerInstance && ServerInstance->SE)
ServerInstance->SE->DelFd(this);
shutdown(this->GetFd(), 2);
close(this->GetFd());
this->SetFd(-1);
/* Rehash the cache */
this->PruneCache();
}
else
{
/* Create initial dns cache */
this->cache = new dnscache();
}
if ((strstr(ServerInstance->Config->DNSServer,"::ffff:") == (char*)&ServerInstance->Config->DNSServer) || (strstr(ServerInstance->Config->DNSServer,"::FFFF:") == (char*)&ServerInstance->Config->DNSServer))
{
ServerInstance->Log(DEFAULT,"WARNING: Using IPv4 addresses over IPv6 forces some DNS checks to be disabled.");
ServerInstance->Log(DEFAULT," This should not cause a problem, however it is recommended you migrate");
ServerInstance->Log(DEFAULT," to a true IPv6 environment.");
this->ip6munge = true;
}
this->socketfamily = AF_INET;
#ifdef IPV6
if (strchr(ServerInstance->Config->DNSServer,':'))
{
this->socketfamily = AF_INET6;
inet_pton(AF_INET6, ServerInstance->Config->DNSServer, &this->myserver6);
}
else
{
inet_aton(ServerInstance->Config->DNSServer, &this->myserver4);
portpass = -1;
}
#else
inet_aton(ServerInstance->Config->DNSServer, &this->myserver4);
#endif
/* Initialize mastersocket */
int s = OpenTCPSocket(ServerInstance->Config->DNSServer, SOCK_DGRAM);
this->SetFd(s);
/* Have we got a socket and is it nonblocking? */
if (this->GetFd() != -1)
{
/* Bind the port - port 0 INADDR_ANY */
if (!ServerInstance->BindSocket(this->GetFd(), portpass, "", false))
{
/* Failed to bind */
shutdown(this->GetFd(),2);
close(this->GetFd());
this->SetFd(-1);
}
if (this->GetFd() >= 0)
{
/* Hook the descriptor into the socket engine */
if (ServerInstance && ServerInstance->SE)
{
if (!ServerInstance->SE->AddFd(this))
{
ServerInstance->Log(DEFAULT,"Internal error starting DNS - hostnames will NOT resolve.");
shutdown(this->GetFd(),2);
close(this->GetFd());
this->SetFd(-1);
}
}
}
}
}
/** Initialise the DNS UDP socket so that we can send requests */
DNS::DNS(InspIRCd* Instance) : ServerInstance(Instance)
{
/* Clear the Resolver class table */
memset(Classes,0,sizeof(Classes));
/* Clear the requests class table */
memset(requests,0,sizeof(requests));
/* Set the id of the next request to 0
*/
currid = 0;
/* DNS::Rehash() sets this to a valid ptr
*/
this->cache = NULL;
/* Again, DNS::Rehash() sets this to a
* valid value
*/
this->SetFd(-1);
/* Actually read the settings
*/
this->Rehash();
this->PruneTimer = new CacheTimer(ServerInstance, this);
ServerInstance->Timers->AddTimer(this->PruneTimer);
}
/** Build a payload to be placed after the header, based upon input data, a resource type, a class and a pointer to a buffer */
int DNS::MakePayload(const char * const name, const QueryType rr, const unsigned short rr_class, unsigned char * const payload)
{
short payloadpos = 0;
const char* tempchr, *tempchr2 = name;
unsigned short length;
/* split name up into labels, create query */
while ((tempchr = strchr(tempchr2,'.')) != NULL)
{
length = tempchr - tempchr2;
if (payloadpos + length + 1 > 507)
return -1;
payload[payloadpos++] = length;
memcpy(&payload[payloadpos],tempchr2,length);
payloadpos += length;
tempchr2 = &tempchr[1];
}
length = strlen(tempchr2);
if (length)
{
if (payloadpos + length + 2 > 507)
return -1;
payload[payloadpos++] = length;
memcpy(&payload[payloadpos],tempchr2,length);
payloadpos += length;
payload[payloadpos++] = 0;
}
if (payloadpos > 508)
return -1;
length = htons(rr);
memcpy(&payload[payloadpos],&length,2);
length = htons(rr_class);
memcpy(&payload[payloadpos + 2],&length,2);
return payloadpos + 4;
}
/** Start lookup of an hostname to an IP address */
int DNS::GetIP(const char *name)
{
DNSHeader h;
int id;
int length;
if ((length = this->MakePayload(name, DNS_QUERY_A, 1, (unsigned char*)&h.payload)) == -1)
return -1;
DNSRequest* req = this->AddQuery(&h, id, name);
if ((!req) || (req->SendRequests(&h, length, DNS_QUERY_A) == -1))
return -1;
return id;
}
/** Start lookup of an hostname to an IPv6 address */
int DNS::GetIP6(const char *name)
{
DNSHeader h;
int id;
int length;
if ((length = this->MakePayload(name, DNS_QUERY_AAAA, 1, (unsigned char*)&h.payload)) == -1)
return -1;
DNSRequest* req = this->AddQuery(&h, id, name);
if ((!req) || (req->SendRequests(&h, length, DNS_QUERY_AAAA) == -1))
return -1;
return id;
}
/** Start lookup of a cname to another name */
int DNS::GetCName(const char *alias)
{
DNSHeader h;
int id;
int length;
if ((length = this->MakePayload(alias, DNS_QUERY_CNAME, 1, (unsigned char*)&h.payload)) == -1)
return -1;
DNSRequest* req = this->AddQuery(&h, id, alias);
if ((!req) || (req->SendRequests(&h, length, DNS_QUERY_CNAME) == -1))
return -1;
return id;
}
/** Start lookup of an IP address to a hostname */
int DNS::GetName(const insp_inaddr *ip)
{
char query[128];
DNSHeader h;
int id;
int length;
#ifdef IPV6
unsigned char* c = (unsigned char*)&ip->s6_addr;
if (c[0] == 0 && c[1] == 0 && c[2] == 0 && c[3] == 0 &&
c[4] == 0 && c[5] == 0 && c[6] == 0 && c[7] == 0 &&
c[8] == 0 && c[9] == 0 && c[10] == 0xFF && c[11] == 0xFF)
sprintf(query,"%d.%d.%d.%d.in-addr.arpa",c[15],c[14],c[13],c[12]);
else
DNS::MakeIP6Int(query, (in6_addr*)ip);
#else
unsigned char* c = (unsigned char*)&ip->s_addr;
sprintf(query,"%d.%d.%d.%d.in-addr.arpa",c[3],c[2],c[1],c[0]);
#endif
if ((length = this->MakePayload(query, DNS_QUERY_PTR, 1, (unsigned char*)&h.payload)) == -1)
return -1;
DNSRequest* req = this->AddQuery(&h, id, insp_ntoa(*ip));
if ((!req) || (req->SendRequests(&h, length, DNS_QUERY_PTR) == -1))
return -1;
return id;
}
/** Start lookup of an IP address to a hostname */
int DNS::GetNameForce(const char *ip, ForceProtocol fp)
{
char query[128];
DNSHeader h;
int id;
int length;
#ifdef SUPPORT_IP6LINKS
if (fp == PROTOCOL_IPV6)
{
in6_addr i;
if (inet_pton(AF_INET6, ip, &i) > 0)
{
DNS::MakeIP6Int(query, &i);
}
else
/* Invalid IP address */
return -1;
}
else
#endif
{
in_addr i;
if (inet_aton(ip, &i))
{
unsigned char* c = (unsigned char*)&i.s_addr;
sprintf(query,"%d.%d.%d.%d.in-addr.arpa",c[3],c[2],c[1],c[0]);
}
else
/* Invalid IP address */
return -1;
}
if ((length = this->MakePayload(query, DNS_QUERY_PTR, 1, (unsigned char*)&h.payload)) == -1)
return -1;
DNSRequest* req = this->AddQuery(&h, id, ip);
if ((!req) || (req->SendRequests(&h, length, DNS_QUERY_PTR) == -1))
return -1;
return id;
}
/** Build an ipv6 reverse domain from an in6_addr
*/
void DNS::MakeIP6Int(char* query, const in6_addr *ip)
{
#ifdef SUPPORT_IP6LINKS
const char* hex = "0123456789abcdef";
for (int index = 31; index >= 0; index--) /* for() loop steps twice per byte */
{
if (index % 2)
/* low nibble */
*query++ = hex[ip->s6_addr[index / 2] & 0x0F];
else
/* high nibble */
*query++ = hex[(ip->s6_addr[index / 2] & 0xF0) >> 4];
*query++ = '.'; /* Seperator */
}
strcpy(query,"ip6.arpa"); /* Suffix the string */
#else
*query = 0;
#endif
}
/** Return the next id which is ready, and the result attached to it */
DNSResult DNS::GetResult()
{
/* Fetch dns query response and decide where it belongs */
DNSHeader header;
DNSRequest *req;
unsigned char buffer[sizeof(DNSHeader)];
sockaddr* from = new sockaddr[2];
#ifdef IPV6
socklen_t x = this->socketfamily == AF_INET ? sizeof(sockaddr_in) : sizeof(sockaddr_in6);
#else
socklen_t x = sizeof(sockaddr_in);
#endif
const char* ipaddr_from;
unsigned short int port_from = 0;
int length = _recvfrom(this->GetFd(),(char*)buffer,sizeof(DNSHeader),0,from,&x);
/* Did we get the whole header? */
if (length < 12)
{
/* Nope - something screwed up. */
delete[] from;
return DNSResult(-1,"",0,"");
}
/* Check wether the reply came from a different DNS
* server to the one we sent it to, or the source-port
* is not 53.
* A user could in theory still spoof dns packets anyway
* but this is less trivial than just sending garbage
* to the client, which is possible without this check.
*
* -- Thanks jilles for pointing this one out.
*/
#ifdef IPV6
char nbuf[MAXBUF];
if (this->socketfamily == AF_INET6)
{
ipaddr_from = inet_ntop(AF_INET6, &((sockaddr_in6*)from)->sin6_addr, nbuf, sizeof(nbuf));
port_from = ntohs(((sockaddr_in6*)from)->sin6_port);
}
else
#endif
{
ipaddr_from = inet_ntoa(((sockaddr_in*)from)->sin_addr);
port_from = ntohs(((sockaddr_in*)from)->sin_port);
}
delete[] from;
/* We cant perform this security check if you're using 4in6.
* Tough luck to you, choose one or't other!
*/
if (!ip6munge)
{
if ((port_from != DNS::QUERY_PORT) || (strcasecmp(ipaddr_from, ServerInstance->Config->DNSServer)))
{
return DNSResult(-1,"",0,"");
}
}
/* Put the read header info into a header class */
DNS::FillHeader(&header,buffer,length - 12);
/* Get the id of this request.
* Its a 16 bit value stored in two char's,
* so we use logic shifts to create the value.
*/
unsigned long this_id = header.id[1] + (header.id[0] << 8);
/* Do we have a pending request matching this id? */
if (!requests[this_id])
{
/* Somehow we got a DNS response for a request we never made... */
return DNSResult(-1,"",0,"");
}
else
{
/* Remove the query from the list of pending queries */
req = requests[this_id];
requests[this_id] = NULL;
}
/* Inform the DNSRequest class that it has a result to be read.
* When its finished it will return a DNSInfo which is a pair of
* unsigned char* resource record data, and an error message.
*/
DNSInfo data = req->ResultIsReady(header, length);
std::string resultstr;
/* Check if we got a result, if we didnt, its an error */
if (data.first == NULL)
{
/* An error.
* Mask the ID with the value of ERROR_MASK, so that
* the dns_deal_with_classes() function knows that its
* an error response and needs to be treated uniquely.
* Put the error message in the second field.
*/
std::string ro = req->orig;
delete req;
return DNSResult(this_id | ERROR_MASK, data.second, 0, ro);
}
else
{
unsigned long ttl = req->ttl;
char formatted[128];
/* Forward lookups come back as binary data. We must format them into ascii */
switch (req->type)
{
case DNS_QUERY_A:
snprintf(formatted,16,"%u.%u.%u.%u",data.first[0],data.first[1],data.first[2],data.first[3]);
resultstr = formatted;
break;
case DNS_QUERY_AAAA:
{
snprintf(formatted,40,"%x:%x:%x:%x:%x:%x:%x:%x",
(ntohs(data.first[0]) + ntohs(data.first[1] << 8)),
(ntohs(data.first[2]) + ntohs(data.first[3] << 8)),
(ntohs(data.first[4]) + ntohs(data.first[5] << 8)),
(ntohs(data.first[6]) + ntohs(data.first[7] << 8)),
(ntohs(data.first[8]) + ntohs(data.first[9] << 8)),
(ntohs(data.first[10]) + ntohs(data.first[11] << 8)),
(ntohs(data.first[12]) + ntohs(data.first[13] << 8)),
(ntohs(data.first[14]) + ntohs(data.first[15] << 8)));
char* c = strstr(formatted,":0:");
if (c != NULL)
{
memmove(c+1,c+2,strlen(c+2) + 1);
c += 2;
while (memcmp(c,"0:",2) == 0)
memmove(c,c+2,strlen(c+2) + 1);
if (memcmp(c,"0",2) == 0)
*c = 0;
if (memcmp(formatted,"0::",3) == 0)
memmove(formatted,formatted + 1, strlen(formatted + 1) + 1);
}
resultstr = formatted;
/* Special case. Sending ::1 around between servers
* and to clients is dangerous, because the : on the
* start makes the client or server interpret the IP
* as the last parameter on the line with a value ":1".
*/
if (*formatted == ':')
resultstr.insert(0, "0");
}
break;
case DNS_QUERY_CNAME:
/* Identical handling to PTR */
case DNS_QUERY_PTR:
/* Reverse lookups just come back as char* */
resultstr = std::string((const char*)data.first);
break;
default:
break;
}
/* Build the reply with the id and hostname/ip in it */
std::string ro = req->orig;
delete req;
return DNSResult(this_id,resultstr,ttl,ro);
}
}
/** A result is ready, process it */
DNSInfo DNSRequest::ResultIsReady(DNSHeader &header, int length)
{
int i = 0;
int q = 0;
int curanswer, o;
ResourceRecord rr;
unsigned short ptr;
/* This is just to keep _FORTIFY_SOURCE happy */
rr.type = DNS_QUERY_NONE;
rr.rdlength = 0;
rr.ttl = 1; /* GCC is a whiney bastard -- see the XXX below. */
if (!(header.flags1 & FLAGS_MASK_QR))
return std::make_pair((unsigned char*)NULL,"Not a query result");
if (header.flags1 & FLAGS_MASK_OPCODE)
return std::make_pair((unsigned char*)NULL,"Unexpected value in DNS reply packet");
if (header.flags2 & FLAGS_MASK_RCODE)
return std::make_pair((unsigned char*)NULL,"Domain name not found");
if (header.ancount < 1)
return std::make_pair((unsigned char*)NULL,"No resource records returned");
/* Subtract the length of the header from the length of the packet */
length -= 12;
while ((unsigned int)q < header.qdcount && i < length)
{
if (header.payload[i] > 63)
{
i += 6;
q++;
}
else
{
if (header.payload[i] == 0)
{
q++;
i += 5;
}
else i += header.payload[i] + 1;
}
}
curanswer = 0;
while ((unsigned)curanswer < header.ancount)
{
q = 0;
while (q == 0 && i < length)
{
if (header.payload[i] > 63)
{
i += 2;
q = 1;
}
else
{
if (header.payload[i] == 0)
{
i++;
q = 1;
}
else i += header.payload[i] + 1; /* skip length and label */
}
}
if (length - i < 10)
return std::make_pair((unsigned char*)NULL,"Incorrectly sized DNS reply");
/* XXX: We actually initialise 'rr' here including its ttl field */
DNS::FillResourceRecord(&rr,&header.payload[i]);
i += 10;
if (rr.type != this->type)
{
curanswer++;
i += rr.rdlength;
continue;
}
if (rr.rr_class != this->rr_class)
{
curanswer++;
i += rr.rdlength;
continue;
}
break;
}
if ((unsigned int)curanswer == header.ancount)
return std::make_pair((unsigned char*)NULL,"No valid answers");
if (i + rr.rdlength > (unsigned int)length)
return std::make_pair((unsigned char*)NULL,"Resource record larger than stated");
if (rr.rdlength > 1023)
return std::make_pair((unsigned char*)NULL,"Resource record too large");
this->ttl = rr.ttl;
switch (rr.type)
{
case DNS_QUERY_CNAME:
/* CNAME and PTR have the same processing code */
case DNS_QUERY_PTR:
o = 0;
q = 0;
while (q == 0 && i < length && o + 256 < 1023)
{
if (header.payload[i] > 63)
{
memcpy(&ptr,&header.payload[i],2);
i = ntohs(ptr) - 0xC000 - 12;
}
else
{
if (header.payload[i] == 0)
{
q = 1;
}
else
{
res[o] = 0;
if (o != 0)
res[o++] = '.';
memcpy(&res[o],&header.payload[i + 1],header.payload[i]);
o += header.payload[i];
i += header.payload[i] + 1;
}
}
}
res[o] = 0;
break;
case DNS_QUERY_AAAA:
memcpy(res,&header.payload[i],rr.rdlength);
res[rr.rdlength] = 0;
break;
case DNS_QUERY_A:
memcpy(res,&header.payload[i],rr.rdlength);
res[rr.rdlength] = 0;
break;
default:
memcpy(res,&header.payload[i],rr.rdlength);
res[rr.rdlength] = 0;
break;
}
return std::make_pair(res,"No error");;
}
/** Close the master socket */
DNS::~DNS()
{
shutdown(this->GetFd(), 2);
close(this->GetFd());
ServerInstance->Timers->DelTimer(this->PruneTimer);
delete this->PruneTimer;
}
CachedQuery* DNS::GetCache(const std::string &source)
{
dnscache::iterator x = cache->find(source.c_str());
if (x != cache->end())
return &(x->second);
else
return NULL;
}
void DNS::DelCache(const std::string &source)
{
cache->erase(source.c_str());
}
void Resolver::TriggerCachedResult()
{
if (CQ)
OnLookupComplete(CQ->data, time_left, true);
}
/** High level abstraction of dns used by application at large */
Resolver::Resolver(InspIRCd* Instance, const std::string &source, QueryType qt, bool &cached, Module* creator) : ServerInstance(Instance), Creator(creator), input(source), querytype(qt)
{
cached = false;
CQ = ServerInstance->Res->GetCache(source);
if (CQ)
{
time_left = CQ->CalcTTLRemaining();
if (!time_left)
{
ServerInstance->Res->DelCache(source);
}
else
{
cached = true;
return;
}
}
insp_inaddr binip;
switch (querytype)
{
case DNS_QUERY_A:
this->myid = ServerInstance->Res->GetIP(source.c_str());
break;
case DNS_QUERY_PTR:
if (insp_aton(source.c_str(), &binip) > 0)
{
/* Valid ip address */
this->myid = ServerInstance->Res->GetName(&binip);
}
else
{
this->OnError(RESOLVER_BADIP, "Bad IP address for reverse lookup");
throw ModuleException("Resolver: Bad IP address");
return;
}
break;
case DNS_QUERY_PTR4:
querytype = DNS_QUERY_PTR;
this->myid = ServerInstance->Res->GetNameForce(source.c_str(), PROTOCOL_IPV4);
break;
case DNS_QUERY_PTR6:
querytype = DNS_QUERY_PTR;
this->myid = ServerInstance->Res->GetNameForce(source.c_str(), PROTOCOL_IPV6);
break;
case DNS_QUERY_AAAA:
this->myid = ServerInstance->Res->GetIP6(source.c_str());
break;
case DNS_QUERY_CNAME:
this->myid = ServerInstance->Res->GetCName(source.c_str());
break;
default:
this->myid = -1;
break;
}
if (this->myid == -1)
{
this->OnError(RESOLVER_NSDOWN, "Nameserver is down");
throw ModuleException("Resolver: Couldnt get an id to make a request");
/* We shouldnt get here really */
return;
}
}
/** Called when an error occurs */
void Resolver::OnError(ResolverError e, const std::string &errormessage)
{
/* Nothing in here */
}
/** Destroy a resolver */
Resolver::~Resolver()
{
/* Nothing here (yet) either */
}
/** Get the request id associated with this class */
int Resolver::GetId()
{
return this->myid;
}
Module* Resolver::GetCreator()
{
return this->Creator;
}
/** Process a socket read event */
void DNS::HandleEvent(EventType et, int errornum)
{
/* Fetch the id and result of the next available packet */
DNSResult res = this->GetResult();
/* Is there a usable request id? */
if (res.id != -1)
{
/* Its an error reply */
if (res.id & ERROR_MASK)
{
/* Mask off the error bit */
res.id -= ERROR_MASK;
/* Marshall the error to the correct class */
if (Classes[res.id])
{
if (ServerInstance && ServerInstance->stats)
ServerInstance->stats->statsDnsBad++;
Classes[res.id]->OnError(RESOLVER_NXDOMAIN, res.result);
delete Classes[res.id];
Classes[res.id] = NULL;
}
}
else
{
/* It is a non-error result, marshall the result to the correct class */
if (Classes[res.id])
{
if (ServerInstance && ServerInstance->stats)
ServerInstance->stats->statsDnsGood++;
if (!this->GetCache(res.original.c_str()))
this->cache->insert(std::make_pair(res.original.c_str(), CachedQuery(res.result, res.ttl)));
Classes[res.id]->OnLookupComplete(res.result, res.ttl, false);
delete Classes[res.id];
Classes[res.id] = NULL;
}
}
if (ServerInstance && ServerInstance->stats)
ServerInstance->stats->statsDns++;
}
}
/** Add a derived Resolver to the working set */
bool DNS::AddResolverClass(Resolver* r)
{
/* Check the pointers validity and the id's validity */
if ((r) && (r->GetId() > -1))
{
/* Check the slot isnt already occupied -
* This should NEVER happen unless we have
* a severely broken DNS server somewhere
*/
if (!Classes[r->GetId()])
{
/* Set up the pointer to the class */
Classes[r->GetId()] = r;
return true;
}
else
/* Duplicate id */
return false;
}
else
{
/* Pointer or id not valid.
* Free the item and return
*/
if (r)
delete r;
return false;
}
}
void DNS::CleanResolvers(Module* module)
{
for (int i = 0; i < MAX_REQUEST_ID; i++)
{
if (Classes[i])
{
if (Classes[i]->GetCreator() == module)
{
Classes[i]->OnError(RESLOVER_FORCEUNLOAD, "Parent module is unloading");
delete Classes[i];
Classes[i] = NULL;
}
}
}
}
/** Generate pseudo-random number */
unsigned long DNS::PRNG()
{
#ifndef WIN32
unsigned long val = 0;
timeval n;
serverstats* s = ServerInstance->stats;
gettimeofday(&n,NULL);
val = (n.tv_usec ^ getpid() ^ geteuid() ^ (this->currid++)) ^ s->statsAccept + n.tv_sec;
val = val + s->statsCollisions ^ s->statsDnsGood - s->statsDnsBad;
val += (s->statsConnects ^ (unsigned long)s->statsSent ^ (unsigned long)s->statsRecv) - ServerInstance->Config->ports.size();
return val;
#else
unsigned long val = 0;
serverstats* s = ServerInstance->stats;
val = (time(NULL) ^ GetCurrentProcessId() ^ GetCurrentThreadId() ^ (this->currid++)) ^ s->statsAccept + time(NULL);
val = val + s->statsCollisions ^ s->statsDnsGood - s->statsDnsBad;
val += (s->statsConnects ^ (unsigned long)s->statsSent ^ (unsigned long)s->statsRecv);
return val;
#endif
}
\ No newline at end of file +/* +------------------------------------+ + * | 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. + * + * --------------------------------------------------- + */ + +/* +dns.cpp - Nonblocking DNS functions. +Very very loosely based on the firedns library, +Copyright (C) 2002 Ian Gulliver. This file is no +longer anything like firedns, there are many major +differences between this code and the original. +Please do not assume that firedns works like this, +looks like this, walks like this or tastes like this. +*/ + +#ifndef WIN32 +#include <sys/types.h> +#include <sys/socket.h> +#include <errno.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#else +#include "inspircd_win32wrapper.h" +#include "inspircd_se_config.h" +#endif + +#include "dns.h" +#include "inspircd.h" +#include "socketengine.h" +#include "configreader.h" +#include "socket.h" + +using irc::sockets::insp_inaddr; +using irc::sockets::insp_ntoa; +using irc::sockets::insp_aton; +using irc::sockets::OpenTCPSocket; + +/** Masks to mask off the responses we get from the DNSRequest methods + */ +enum QueryInfo +{ + ERROR_MASK = 0x10000 /* Result is an error */ +}; + +/** Flags which can be ORed into a request or reply for different meanings + */ +enum QueryFlags +{ + FLAGS_MASK_RD = 0x01, /* Recursive */ + FLAGS_MASK_TC = 0x02, + FLAGS_MASK_AA = 0x04, /* Authoritative */ + FLAGS_MASK_OPCODE = 0x78, + FLAGS_MASK_QR = 0x80, + FLAGS_MASK_RCODE = 0x0F, /* Request */ + FLAGS_MASK_Z = 0x70, + FLAGS_MASK_RA = 0x80 +}; + + +/** Represents a dns resource record (rr) + */ +struct ResourceRecord +{ + QueryType type; /* Record type */ + unsigned int rr_class; /* Record class */ + unsigned long ttl; /* Time to live */ + unsigned int rdlength; /* Record length */ +}; + +/** Represents a dns request/reply header, and its payload as opaque data. + */ +class DNSHeader +{ + public: + unsigned char id[2]; /* Request id */ + unsigned int flags1; /* Flags */ + unsigned int flags2; /* Flags */ + unsigned int qdcount; + unsigned int ancount; /* Answer count */ + unsigned int nscount; /* Nameserver count */ + unsigned int arcount; + unsigned char payload[512]; /* Packet payload */ +}; + +class DNSRequest +{ + public: + unsigned char id[2]; /* Request id */ + unsigned char* res; /* Result processing buffer */ + unsigned int rr_class; /* Request class */ + QueryType type; /* Request type */ + DNS* dnsobj; /* DNS caller (where we get our FD from) */ + unsigned long ttl; /* Time to live */ + std::string orig; /* Original requested name/ip */ + + DNSRequest(InspIRCd* Instance, DNS* dns, int id, const std::string &original); + ~DNSRequest(); + DNSInfo ResultIsReady(DNSHeader &h, int length); + int SendRequests(const DNSHeader *header, const int length, QueryType qt); +}; + +class CacheTimer : public InspTimer +{ + private: + InspIRCd* ServerInstance; + DNS* dns; + public: + CacheTimer(InspIRCd* Instance, DNS* thisdns) + : InspTimer(3600, Instance->Time(), true), ServerInstance(Instance), dns(thisdns) { } + + virtual void Tick(time_t TIME) + { + dns->PruneCache(); + } +}; + +class RequestTimeout : public InspTimer +{ + InspIRCd* ServerInstance; + DNSRequest* watch; + int watchid; + public: + RequestTimeout(unsigned long n, InspIRCd* SI, DNSRequest* watching, int id) : InspTimer(n, time(NULL)), ServerInstance(SI), watch(watching), watchid(id) + { + } + + void Tick(time_t TIME) + { + if (ServerInstance->Res->requests[watchid] == watch) + { + /* Still exists, whack it */ + if (ServerInstance->Res->Classes[watchid]) + { + ServerInstance->Res->Classes[watchid]->OnError(RESOLVER_TIMEOUT, "Request timed out"); + delete ServerInstance->Res->Classes[watchid]; + ServerInstance->Res->Classes[watchid] = NULL; + } + ServerInstance->Res->requests[watchid] = NULL; + DELETE(watch); + return; + } + } +}; + +/* Allocate the processing buffer */ +DNSRequest::DNSRequest(InspIRCd* Instance, DNS* dns, int id, const std::string &original) : dnsobj(dns) +{ + res = new unsigned char[512]; + *res = 0; + orig = original; + RequestTimeout* RT = new RequestTimeout(Instance->Config->dns_timeout ? Instance->Config->dns_timeout : 5, Instance, this, id); + Instance->Timers->AddTimer(RT); /* The timer manager frees this */ +} + +/* Deallocate the processing buffer */ +DNSRequest::~DNSRequest() +{ + delete[] res; +} + +/** Fill a ResourceRecord class based on raw data input */ +inline void DNS::FillResourceRecord(ResourceRecord* rr, const unsigned char *input) +{ + rr->type = (QueryType)((input[0] << 8) + input[1]); + rr->rr_class = (input[2] << 8) + input[3]; + rr->ttl = (input[4] << 24) + (input[5] << 16) + (input[6] << 8) + input[7]; + rr->rdlength = (input[8] << 8) + input[9]; +} + +/** Fill a DNSHeader class based on raw data input of a given length */ +inline void DNS::FillHeader(DNSHeader *header, const unsigned char *input, const int length) +{ + header->id[0] = input[0]; + header->id[1] = input[1]; + header->flags1 = input[2]; + header->flags2 = input[3]; + header->qdcount = (input[4] << 8) + input[5]; + header->ancount = (input[6] << 8) + input[7]; + header->nscount = (input[8] << 8) + input[9]; + header->arcount = (input[10] << 8) + input[11]; + memcpy(header->payload,&input[12],length); +} + +/** Empty a DNSHeader class out into raw data, ready for transmission */ +inline void DNS::EmptyHeader(unsigned char *output, const DNSHeader *header, const int length) +{ + output[0] = header->id[0]; + output[1] = header->id[1]; + output[2] = header->flags1; + output[3] = header->flags2; + output[4] = header->qdcount >> 8; + output[5] = header->qdcount & 0xFF; + output[6] = header->ancount >> 8; + output[7] = header->ancount & 0xFF; + output[8] = header->nscount >> 8; + output[9] = header->nscount & 0xFF; + output[10] = header->arcount >> 8; + output[11] = header->arcount & 0xFF; + memcpy(&output[12],header->payload,length); +} + +/** Send requests we have previously built down the UDP socket */ +int DNSRequest::SendRequests(const DNSHeader *header, const int length, QueryType qt) +{ + unsigned char payload[sizeof(DNSHeader)]; + + this->rr_class = 1; + this->type = qt; + + DNS::EmptyHeader(payload,header,length); + +#ifdef IPV6 + if (this->dnsobj->socketfamily == AF_INET6) + { + sockaddr_in6 addr; + memset(&addr,0,sizeof(addr)); + memcpy(&addr.sin6_addr,&dnsobj->myserver6,sizeof(addr.sin6_addr)); + addr.sin6_family = AF_INET6; + addr.sin6_port = htons(DNS::QUERY_PORT); + if (sendto(dnsobj->GetFd(), payload, length + 12, 0, (sockaddr *) &addr, sizeof(addr)) != length+12) + return -1; + } + else + { + sockaddr_in addr; + memset(&addr,0,sizeof(addr)); + memcpy(&addr.sin_addr.s_addr,&dnsobj->myserver4,sizeof(addr.sin_addr)); + addr.sin_family = AF_INET; + addr.sin_port = htons(DNS::QUERY_PORT); + if (sendto(dnsobj->GetFd(), payload, length + 12, 0, (sockaddr *) &addr, sizeof(addr)) != length+12) + return -1; + } +#else + sockaddr_in addr; + memset(&addr,0,sizeof(addr)); + memcpy(&addr.sin_addr.s_addr, &dnsobj->myserver4, sizeof(addr.sin_addr)); + addr.sin_family = AF_INET; + addr.sin_port = htons(DNS::QUERY_PORT); + if (sendto(dnsobj->GetFd(), (const char*)payload, length + 12, 0, (sockaddr *) &addr, sizeof(addr)) != length+12) + return -1; +#endif + + return 0; +} + +/** Add a query with a predefined header, and allocate an ID for it. */ +DNSRequest* DNS::AddQuery(DNSHeader *header, int &id, const char* original) +{ + /* Is the DNS connection down? */ + if (this->GetFd() == -1) + return NULL; + + /* Create an id */ + id = this->PRNG() & DNS::MAX_REQUEST_ID; + + /* If this id is already 'in flight', pick another. */ + while (requests[id]) + id = this->PRNG() & DNS::MAX_REQUEST_ID; + + DNSRequest* req = new DNSRequest(ServerInstance, this, id, original); + + header->id[0] = req->id[0] = id >> 8; + header->id[1] = req->id[1] = id & 0xFF; + header->flags1 = FLAGS_MASK_RD; + header->flags2 = 0; + header->qdcount = 1; + header->ancount = 0; + header->nscount = 0; + header->arcount = 0; + + /* At this point we already know the id doesnt exist, + * so there needs to be no second check for the ::end() + */ + requests[id] = req; + + /* According to the C++ spec, new never returns NULL. */ + return req; +} + +int DNS::ClearCache() +{ + /* This ensures the buckets are reset to sane levels */ + int rv = this->cache->size(); + delete this->cache; + this->cache = new dnscache(); + return rv; +} + +int DNS::PruneCache() +{ + int n = 0; + dnscache* newcache = new dnscache(); + for (dnscache::iterator i = this->cache->begin(); i != this->cache->end(); i++) + /* Dont include expired items (theres no point) */ + if (i->second.CalcTTLRemaining()) + newcache->insert(*i); + else + n++; + + delete this->cache; + this->cache = newcache; + return n; +} + +void DNS::Rehash() +{ + ip6munge = false; + int portpass = 0; + + if (this->GetFd() > -1) + { + if (ServerInstance && ServerInstance->SE) + ServerInstance->SE->DelFd(this); + shutdown(this->GetFd(), 2); + close(this->GetFd()); + this->SetFd(-1); + + /* Rehash the cache */ + this->PruneCache(); + } + else + { + /* Create initial dns cache */ + this->cache = new dnscache(); + } + + if ((strstr(ServerInstance->Config->DNSServer,"::ffff:") == (char*)&ServerInstance->Config->DNSServer) || (strstr(ServerInstance->Config->DNSServer,"::FFFF:") == (char*)&ServerInstance->Config->DNSServer)) + { + ServerInstance->Log(DEFAULT,"WARNING: Using IPv4 addresses over IPv6 forces some DNS checks to be disabled."); + ServerInstance->Log(DEFAULT," This should not cause a problem, however it is recommended you migrate"); + ServerInstance->Log(DEFAULT," to a true IPv6 environment."); + this->ip6munge = true; + } + + this->socketfamily = AF_INET; +#ifdef IPV6 + if (strchr(ServerInstance->Config->DNSServer,':')) + { + this->socketfamily = AF_INET6; + inet_pton(AF_INET6, ServerInstance->Config->DNSServer, &this->myserver6); + } + else + { + inet_aton(ServerInstance->Config->DNSServer, &this->myserver4); + portpass = -1; + } +#else + inet_aton(ServerInstance->Config->DNSServer, &this->myserver4); +#endif + + /* Initialize mastersocket */ + int s = OpenTCPSocket(ServerInstance->Config->DNSServer, SOCK_DGRAM); + this->SetFd(s); + + /* Have we got a socket and is it nonblocking? */ + if (this->GetFd() != -1) + { + /* Bind the port - port 0 INADDR_ANY */ + if (!ServerInstance->BindSocket(this->GetFd(), portpass, "", false)) + { + /* Failed to bind */ + shutdown(this->GetFd(),2); + close(this->GetFd()); + this->SetFd(-1); + } + + if (this->GetFd() >= 0) + { + /* Hook the descriptor into the socket engine */ + if (ServerInstance && ServerInstance->SE) + { + if (!ServerInstance->SE->AddFd(this)) + { + ServerInstance->Log(DEFAULT,"Internal error starting DNS - hostnames will NOT resolve."); + shutdown(this->GetFd(),2); + close(this->GetFd()); + this->SetFd(-1); + } + } + } + } +} + +/** Initialise the DNS UDP socket so that we can send requests */ +DNS::DNS(InspIRCd* Instance) : ServerInstance(Instance) +{ + /* Clear the Resolver class table */ + memset(Classes,0,sizeof(Classes)); + + /* Clear the requests class table */ + memset(requests,0,sizeof(requests)); + + /* Set the id of the next request to 0 + */ + currid = 0; + + /* DNS::Rehash() sets this to a valid ptr + */ + this->cache = NULL; + + /* Again, DNS::Rehash() sets this to a + * valid value + */ + this->SetFd(-1); + + /* Actually read the settings + */ + this->Rehash(); + + this->PruneTimer = new CacheTimer(ServerInstance, this); + + ServerInstance->Timers->AddTimer(this->PruneTimer); +} + +/** Build a payload to be placed after the header, based upon input data, a resource type, a class and a pointer to a buffer */ +int DNS::MakePayload(const char * const name, const QueryType rr, const unsigned short rr_class, unsigned char * const payload) +{ + short payloadpos = 0; + const char* tempchr, *tempchr2 = name; + unsigned short length; + + /* split name up into labels, create query */ + while ((tempchr = strchr(tempchr2,'.')) != NULL) + { + length = tempchr - tempchr2; + if (payloadpos + length + 1 > 507) + return -1; + payload[payloadpos++] = length; + memcpy(&payload[payloadpos],tempchr2,length); + payloadpos += length; + tempchr2 = &tempchr[1]; + } + length = strlen(tempchr2); + if (length) + { + if (payloadpos + length + 2 > 507) + return -1; + payload[payloadpos++] = length; + memcpy(&payload[payloadpos],tempchr2,length); + payloadpos += length; + payload[payloadpos++] = 0; + } + if (payloadpos > 508) + return -1; + length = htons(rr); + memcpy(&payload[payloadpos],&length,2); + length = htons(rr_class); + memcpy(&payload[payloadpos + 2],&length,2); + return payloadpos + 4; +} + +/** Start lookup of an hostname to an IP address */ +int DNS::GetIP(const char *name) +{ + DNSHeader h; + int id; + int length; + + if ((length = this->MakePayload(name, DNS_QUERY_A, 1, (unsigned char*)&h.payload)) == -1) + return -1; + + DNSRequest* req = this->AddQuery(&h, id, name); + + if ((!req) || (req->SendRequests(&h, length, DNS_QUERY_A) == -1)) + return -1; + + return id; +} + +/** Start lookup of an hostname to an IPv6 address */ +int DNS::GetIP6(const char *name) +{ + DNSHeader h; + int id; + int length; + + if ((length = this->MakePayload(name, DNS_QUERY_AAAA, 1, (unsigned char*)&h.payload)) == -1) + return -1; + + DNSRequest* req = this->AddQuery(&h, id, name); + + if ((!req) || (req->SendRequests(&h, length, DNS_QUERY_AAAA) == -1)) + return -1; + + return id; +} + +/** Start lookup of a cname to another name */ +int DNS::GetCName(const char *alias) +{ + DNSHeader h; + int id; + int length; + + if ((length = this->MakePayload(alias, DNS_QUERY_CNAME, 1, (unsigned char*)&h.payload)) == -1) + return -1; + + DNSRequest* req = this->AddQuery(&h, id, alias); + + if ((!req) || (req->SendRequests(&h, length, DNS_QUERY_CNAME) == -1)) + return -1; + + return id; +} + +/** Start lookup of an IP address to a hostname */ +int DNS::GetName(const insp_inaddr *ip) +{ + char query[128]; + DNSHeader h; + int id; + int length; + +#ifdef IPV6 + unsigned char* c = (unsigned char*)&ip->s6_addr; + if (c[0] == 0 && c[1] == 0 && c[2] == 0 && c[3] == 0 && + c[4] == 0 && c[5] == 0 && c[6] == 0 && c[7] == 0 && + c[8] == 0 && c[9] == 0 && c[10] == 0xFF && c[11] == 0xFF) + sprintf(query,"%d.%d.%d.%d.in-addr.arpa",c[15],c[14],c[13],c[12]); + else + DNS::MakeIP6Int(query, (in6_addr*)ip); +#else + unsigned char* c = (unsigned char*)&ip->s_addr; + sprintf(query,"%d.%d.%d.%d.in-addr.arpa",c[3],c[2],c[1],c[0]); +#endif + + if ((length = this->MakePayload(query, DNS_QUERY_PTR, 1, (unsigned char*)&h.payload)) == -1) + return -1; + + DNSRequest* req = this->AddQuery(&h, id, insp_ntoa(*ip)); + + if ((!req) || (req->SendRequests(&h, length, DNS_QUERY_PTR) == -1)) + return -1; + + return id; +} + +/** Start lookup of an IP address to a hostname */ +int DNS::GetNameForce(const char *ip, ForceProtocol fp) +{ + char query[128]; + DNSHeader h; + int id; + int length; +#ifdef SUPPORT_IP6LINKS + if (fp == PROTOCOL_IPV6) + { + in6_addr i; + if (inet_pton(AF_INET6, ip, &i) > 0) + { + DNS::MakeIP6Int(query, &i); + } + else + /* Invalid IP address */ + return -1; + } + else +#endif + { + in_addr i; + if (inet_aton(ip, &i)) + { + unsigned char* c = (unsigned char*)&i.s_addr; + sprintf(query,"%d.%d.%d.%d.in-addr.arpa",c[3],c[2],c[1],c[0]); + } + else + /* Invalid IP address */ + return -1; + } + + if ((length = this->MakePayload(query, DNS_QUERY_PTR, 1, (unsigned char*)&h.payload)) == -1) + return -1; + + DNSRequest* req = this->AddQuery(&h, id, ip); + + if ((!req) || (req->SendRequests(&h, length, DNS_QUERY_PTR) == -1)) + return -1; + + return id; +} + +/** Build an ipv6 reverse domain from an in6_addr + */ +void DNS::MakeIP6Int(char* query, const in6_addr *ip) +{ +#ifdef SUPPORT_IP6LINKS + const char* hex = "0123456789abcdef"; + for (int index = 31; index >= 0; index--) /* for() loop steps twice per byte */ + { + if (index % 2) + /* low nibble */ + *query++ = hex[ip->s6_addr[index / 2] & 0x0F]; + else + /* high nibble */ + *query++ = hex[(ip->s6_addr[index / 2] & 0xF0) >> 4]; + *query++ = '.'; /* Seperator */ + } + strcpy(query,"ip6.arpa"); /* Suffix the string */ +#else + *query = 0; +#endif +} + +/** Return the next id which is ready, and the result attached to it */ +DNSResult DNS::GetResult() +{ + /* Fetch dns query response and decide where it belongs */ + DNSHeader header; + DNSRequest *req; + unsigned char buffer[sizeof(DNSHeader)]; + sockaddr* from = new sockaddr[2]; +#ifdef IPV6 + socklen_t x = this->socketfamily == AF_INET ? sizeof(sockaddr_in) : sizeof(sockaddr_in6); +#else + socklen_t x = sizeof(sockaddr_in); +#endif + const char* ipaddr_from; + unsigned short int port_from = 0; + int length = _recvfrom(this->GetFd(),(char*)buffer,sizeof(DNSHeader),0,from,&x); + + /* Did we get the whole header? */ + if (length < 12) + { + /* Nope - something screwed up. */ + delete[] from; + return DNSResult(-1,"",0,""); + } + + /* Check wether the reply came from a different DNS + * server to the one we sent it to, or the source-port + * is not 53. + * A user could in theory still spoof dns packets anyway + * but this is less trivial than just sending garbage + * to the client, which is possible without this check. + * + * -- Thanks jilles for pointing this one out. + */ +#ifdef IPV6 + char nbuf[MAXBUF]; + if (this->socketfamily == AF_INET6) + { + ipaddr_from = inet_ntop(AF_INET6, &((sockaddr_in6*)from)->sin6_addr, nbuf, sizeof(nbuf)); + port_from = ntohs(((sockaddr_in6*)from)->sin6_port); + } + else +#endif + { + ipaddr_from = inet_ntoa(((sockaddr_in*)from)->sin_addr); + port_from = ntohs(((sockaddr_in*)from)->sin_port); + } + + delete[] from; + + /* We cant perform this security check if you're using 4in6. + * Tough luck to you, choose one or't other! + */ + if (!ip6munge) + { + if ((port_from != DNS::QUERY_PORT) || (strcasecmp(ipaddr_from, ServerInstance->Config->DNSServer))) + { + return DNSResult(-1,"",0,""); + } + } + + /* Put the read header info into a header class */ + DNS::FillHeader(&header,buffer,length - 12); + + /* Get the id of this request. + * Its a 16 bit value stored in two char's, + * so we use logic shifts to create the value. + */ + unsigned long this_id = header.id[1] + (header.id[0] << 8); + + /* Do we have a pending request matching this id? */ + if (!requests[this_id]) + { + /* Somehow we got a DNS response for a request we never made... */ + return DNSResult(-1,"",0,""); + } + else + { + /* Remove the query from the list of pending queries */ + req = requests[this_id]; + requests[this_id] = NULL; + } + + /* Inform the DNSRequest class that it has a result to be read. + * When its finished it will return a DNSInfo which is a pair of + * unsigned char* resource record data, and an error message. + */ + DNSInfo data = req->ResultIsReady(header, length); + std::string resultstr; + + /* Check if we got a result, if we didnt, its an error */ + if (data.first == NULL) + { + /* An error. + * Mask the ID with the value of ERROR_MASK, so that + * the dns_deal_with_classes() function knows that its + * an error response and needs to be treated uniquely. + * Put the error message in the second field. + */ + std::string ro = req->orig; + delete req; + return DNSResult(this_id | ERROR_MASK, data.second, 0, ro); + } + else + { + unsigned long ttl = req->ttl; + char formatted[128]; + + /* Forward lookups come back as binary data. We must format them into ascii */ + switch (req->type) + { + case DNS_QUERY_A: + snprintf(formatted,16,"%u.%u.%u.%u",data.first[0],data.first[1],data.first[2],data.first[3]); + resultstr = formatted; + break; + + case DNS_QUERY_AAAA: + { + snprintf(formatted,40,"%x:%x:%x:%x:%x:%x:%x:%x", + (ntohs(data.first[0]) + ntohs(data.first[1] << 8)), + (ntohs(data.first[2]) + ntohs(data.first[3] << 8)), + (ntohs(data.first[4]) + ntohs(data.first[5] << 8)), + (ntohs(data.first[6]) + ntohs(data.first[7] << 8)), + (ntohs(data.first[8]) + ntohs(data.first[9] << 8)), + (ntohs(data.first[10]) + ntohs(data.first[11] << 8)), + (ntohs(data.first[12]) + ntohs(data.first[13] << 8)), + (ntohs(data.first[14]) + ntohs(data.first[15] << 8))); + char* c = strstr(formatted,":0:"); + if (c != NULL) + { + memmove(c+1,c+2,strlen(c+2) + 1); + c += 2; + while (memcmp(c,"0:",2) == 0) + memmove(c,c+2,strlen(c+2) + 1); + if (memcmp(c,"0",2) == 0) + *c = 0; + if (memcmp(formatted,"0::",3) == 0) + memmove(formatted,formatted + 1, strlen(formatted + 1) + 1); + } + resultstr = formatted; + + /* Special case. Sending ::1 around between servers + * and to clients is dangerous, because the : on the + * start makes the client or server interpret the IP + * as the last parameter on the line with a value ":1". + */ + if (*formatted == ':') + resultstr.insert(0, "0"); + } + break; + + case DNS_QUERY_CNAME: + /* Identical handling to PTR */ + + case DNS_QUERY_PTR: + /* Reverse lookups just come back as char* */ + resultstr = std::string((const char*)data.first); + break; + + default: + break; + + } + + /* Build the reply with the id and hostname/ip in it */ + std::string ro = req->orig; + delete req; + return DNSResult(this_id,resultstr,ttl,ro); + } +} + +/** A result is ready, process it */ +DNSInfo DNSRequest::ResultIsReady(DNSHeader &header, int length) +{ + int i = 0; + int q = 0; + int curanswer, o; + ResourceRecord rr; + unsigned short ptr; + + /* This is just to keep _FORTIFY_SOURCE happy */ + rr.type = DNS_QUERY_NONE; + rr.rdlength = 0; + rr.ttl = 1; /* GCC is a whiney bastard -- see the XXX below. */ + + if (!(header.flags1 & FLAGS_MASK_QR)) + return std::make_pair((unsigned char*)NULL,"Not a query result"); + + if (header.flags1 & FLAGS_MASK_OPCODE) + return std::make_pair((unsigned char*)NULL,"Unexpected value in DNS reply packet"); + + if (header.flags2 & FLAGS_MASK_RCODE) + return std::make_pair((unsigned char*)NULL,"Domain name not found"); + + if (header.ancount < 1) + return std::make_pair((unsigned char*)NULL,"No resource records returned"); + + /* Subtract the length of the header from the length of the packet */ + length -= 12; + + while ((unsigned int)q < header.qdcount && i < length) + { + if (header.payload[i] > 63) + { + i += 6; + q++; + } + else + { + if (header.payload[i] == 0) + { + q++; + i += 5; + } + else i += header.payload[i] + 1; + } + } + curanswer = 0; + while ((unsigned)curanswer < header.ancount) + { + q = 0; + while (q == 0 && i < length) + { + if (header.payload[i] > 63) + { + i += 2; + q = 1; + } + else + { + if (header.payload[i] == 0) + { + i++; + q = 1; + } + else i += header.payload[i] + 1; /* skip length and label */ + } + } + if (length - i < 10) + return std::make_pair((unsigned char*)NULL,"Incorrectly sized DNS reply"); + + /* XXX: We actually initialise 'rr' here including its ttl field */ + DNS::FillResourceRecord(&rr,&header.payload[i]); + i += 10; + if (rr.type != this->type) + { + curanswer++; + i += rr.rdlength; + continue; + } + if (rr.rr_class != this->rr_class) + { + curanswer++; + i += rr.rdlength; + continue; + } + break; + } + if ((unsigned int)curanswer == header.ancount) + return std::make_pair((unsigned char*)NULL,"No valid answers"); + + if (i + rr.rdlength > (unsigned int)length) + return std::make_pair((unsigned char*)NULL,"Resource record larger than stated"); + + if (rr.rdlength > 1023) + return std::make_pair((unsigned char*)NULL,"Resource record too large"); + + this->ttl = rr.ttl; + + switch (rr.type) + { + case DNS_QUERY_CNAME: + /* CNAME and PTR have the same processing code */ + case DNS_QUERY_PTR: + o = 0; + q = 0; + while (q == 0 && i < length && o + 256 < 1023) + { + if (header.payload[i] > 63) + { + memcpy(&ptr,&header.payload[i],2); + i = ntohs(ptr) - 0xC000 - 12; + } + else + { + if (header.payload[i] == 0) + { + q = 1; + } + else + { + res[o] = 0; + if (o != 0) + res[o++] = '.'; + memcpy(&res[o],&header.payload[i + 1],header.payload[i]); + o += header.payload[i]; + i += header.payload[i] + 1; + } + } + } + res[o] = 0; + break; + case DNS_QUERY_AAAA: + memcpy(res,&header.payload[i],rr.rdlength); + res[rr.rdlength] = 0; + break; + case DNS_QUERY_A: + memcpy(res,&header.payload[i],rr.rdlength); + res[rr.rdlength] = 0; + break; + default: + memcpy(res,&header.payload[i],rr.rdlength); + res[rr.rdlength] = 0; + break; + } + return std::make_pair(res,"No error");; +} + +/** Close the master socket */ +DNS::~DNS() +{ + shutdown(this->GetFd(), 2); + close(this->GetFd()); + ServerInstance->Timers->DelTimer(this->PruneTimer); + delete this->PruneTimer; +} + +CachedQuery* DNS::GetCache(const std::string &source) +{ + dnscache::iterator x = cache->find(source.c_str()); + if (x != cache->end()) + return &(x->second); + else + return NULL; +} + +void DNS::DelCache(const std::string &source) +{ + cache->erase(source.c_str()); +} + +void Resolver::TriggerCachedResult() +{ + if (CQ) + OnLookupComplete(CQ->data, time_left, true); +} + +/** High level abstraction of dns used by application at large */ +Resolver::Resolver(InspIRCd* Instance, const std::string &source, QueryType qt, bool &cached, Module* creator) : ServerInstance(Instance), Creator(creator), input(source), querytype(qt) +{ + cached = false; + + CQ = ServerInstance->Res->GetCache(source); + if (CQ) + { + time_left = CQ->CalcTTLRemaining(); + if (!time_left) + { + ServerInstance->Res->DelCache(source); + } + else + { + cached = true; + return; + } + } + + insp_inaddr binip; + + switch (querytype) + { + case DNS_QUERY_A: + this->myid = ServerInstance->Res->GetIP(source.c_str()); + break; + + case DNS_QUERY_PTR: + if (insp_aton(source.c_str(), &binip) > 0) + { + /* Valid ip address */ + this->myid = ServerInstance->Res->GetName(&binip); + } + else + { + this->OnError(RESOLVER_BADIP, "Bad IP address for reverse lookup"); + throw ModuleException("Resolver: Bad IP address"); + return; + } + break; + + case DNS_QUERY_PTR4: + querytype = DNS_QUERY_PTR; + this->myid = ServerInstance->Res->GetNameForce(source.c_str(), PROTOCOL_IPV4); + break; + + case DNS_QUERY_PTR6: + querytype = DNS_QUERY_PTR; + this->myid = ServerInstance->Res->GetNameForce(source.c_str(), PROTOCOL_IPV6); + break; + + case DNS_QUERY_AAAA: + this->myid = ServerInstance->Res->GetIP6(source.c_str()); + break; + + case DNS_QUERY_CNAME: + this->myid = ServerInstance->Res->GetCName(source.c_str()); + break; + + default: + this->myid = -1; + break; + } + if (this->myid == -1) + { + this->OnError(RESOLVER_NSDOWN, "Nameserver is down"); + throw ModuleException("Resolver: Couldnt get an id to make a request"); + /* We shouldnt get here really */ + return; + } +} + +/** Called when an error occurs */ +void Resolver::OnError(ResolverError e, const std::string &errormessage) +{ + /* Nothing in here */ +} + +/** Destroy a resolver */ +Resolver::~Resolver() +{ + /* Nothing here (yet) either */ +} + +/** Get the request id associated with this class */ +int Resolver::GetId() +{ + return this->myid; +} + +Module* Resolver::GetCreator() +{ + return this->Creator; +} + +/** Process a socket read event */ +void DNS::HandleEvent(EventType et, int errornum) +{ + /* Fetch the id and result of the next available packet */ + DNSResult res = this->GetResult(); + /* Is there a usable request id? */ + if (res.id != -1) + { + /* Its an error reply */ + if (res.id & ERROR_MASK) + { + /* Mask off the error bit */ + res.id -= ERROR_MASK; + /* Marshall the error to the correct class */ + if (Classes[res.id]) + { + if (ServerInstance && ServerInstance->stats) + ServerInstance->stats->statsDnsBad++; + Classes[res.id]->OnError(RESOLVER_NXDOMAIN, res.result); + delete Classes[res.id]; + Classes[res.id] = NULL; + } + } + else + { + /* It is a non-error result, marshall the result to the correct class */ + if (Classes[res.id]) + { + if (ServerInstance && ServerInstance->stats) + ServerInstance->stats->statsDnsGood++; + + if (!this->GetCache(res.original.c_str())) + this->cache->insert(std::make_pair(res.original.c_str(), CachedQuery(res.result, res.ttl))); + + Classes[res.id]->OnLookupComplete(res.result, res.ttl, false); + delete Classes[res.id]; + Classes[res.id] = NULL; + } + } + + if (ServerInstance && ServerInstance->stats) + ServerInstance->stats->statsDns++; + } +} + +/** Add a derived Resolver to the working set */ +bool DNS::AddResolverClass(Resolver* r) +{ + /* Check the pointers validity and the id's validity */ + if ((r) && (r->GetId() > -1)) + { + /* Check the slot isnt already occupied - + * This should NEVER happen unless we have + * a severely broken DNS server somewhere + */ + if (!Classes[r->GetId()]) + { + /* Set up the pointer to the class */ + Classes[r->GetId()] = r; + return true; + } + else + /* Duplicate id */ + return false; + } + else + { + /* Pointer or id not valid. + * Free the item and return + */ + if (r) + delete r; + + return false; + } +} + +void DNS::CleanResolvers(Module* module) +{ + for (int i = 0; i < MAX_REQUEST_ID; i++) + { + if (Classes[i]) + { + if (Classes[i]->GetCreator() == module) + { + Classes[i]->OnError(RESLOVER_FORCEUNLOAD, "Parent module is unloading"); + delete Classes[i]; + Classes[i] = NULL; + } + } + } +} + +/** Generate pseudo-random number */ +unsigned long DNS::PRNG() +{ +#ifndef WIN32 + unsigned long val = 0; + timeval n; + serverstats* s = ServerInstance->stats; + gettimeofday(&n,NULL); + val = (n.tv_usec ^ getpid() ^ geteuid() ^ (this->currid++)) ^ s->statsAccept + n.tv_sec; + val = val + s->statsCollisions ^ s->statsDnsGood - s->statsDnsBad; + val += (s->statsConnects ^ (unsigned long)s->statsSent ^ (unsigned long)s->statsRecv) - ServerInstance->Config->ports.size(); + return val; +#else + unsigned long val = 0; + serverstats* s = ServerInstance->stats; + val = (time(NULL) ^ GetCurrentProcessId() ^ GetCurrentThreadId() ^ (this->currid++)) ^ s->statsAccept + time(NULL); + val = val + s->statsCollisions ^ s->statsDnsGood - s->statsDnsBad; + val += (s->statsConnects ^ (unsigned long)s->statsSent ^ (unsigned long)s->statsRecv); + return val; +#endif +} + diff --git a/src/dynamic.cpp b/src/dynamic.cpp index 5d7759b47..179113cae 100644 --- a/src/dynamic.cpp +++ b/src/dynamic.cpp @@ -1 +1,89 @@ -/* +------------------------------------+
* | 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 "dynamic.h"
#ifndef WIN32
#include <dlfcn.h>
#endif
DLLManager::DLLManager(InspIRCd* ServerInstance, const char *fname)
{
err = NULL;
if (!strstr(fname,".so"))
{
err = "This doesn't look like a module file to me...";
return;
}
h = dlopen(fname, RTLD_NOW|RTLD_LOCAL);
if (!h)
{
err = (char*)dlerror();
return;
}
}
DLLManager::~DLLManager()
{
// close the library if it isn't null
if (h)
dlclose(h);
}
bool DLLManager::GetSymbol(void** v, const char* sym_name)
{
// try extract a symbol from the library
// get any error message is there is any
if (h)
{
dlerror(); // clear value
*v = dlsym(h, sym_name);
err = (char*)dlerror();
if (!*v || err)
return false;
}
if (err)
{
return false;
}
else
{
return true;
}
}
DLLFactoryBase::DLLFactoryBase(InspIRCd* Instance, const char* fname, const char* symbol) : DLLManager(Instance, fname)
{
// try get the factory function if there is no error yet
factory_func = 0;
if (!LastError())
{
if (!GetSymbol( (void **)&factory_func, symbol ? symbol : "init_module"))
{
throw ModuleException("Missing init_module() entrypoint!");
}
}
}
DLLFactoryBase::~DLLFactoryBase()
{
}
\ No newline at end of file +/* +------------------------------------+ + * | 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 "dynamic.h" +#ifndef WIN32 +#include <dlfcn.h> +#endif + +DLLManager::DLLManager(InspIRCd* ServerInstance, const char *fname) +{ + err = NULL; + + if (!strstr(fname,".so")) + { + err = "This doesn't look like a module file to me..."; + return; + } + + h = dlopen(fname, RTLD_NOW|RTLD_LOCAL); + if (!h) + { + err = (char*)dlerror(); + return; + } +} + +DLLManager::~DLLManager() +{ + // close the library if it isn't null + if (h) + dlclose(h); +} + + + +bool DLLManager::GetSymbol(void** v, const char* sym_name) +{ + // try extract a symbol from the library + // get any error message is there is any + + if (h) + { + dlerror(); // clear value + *v = dlsym(h, sym_name); + err = (char*)dlerror(); + if (!*v || err) + return false; + } + + if (err) + { + return false; + } + else + { + return true; + } +} + +DLLFactoryBase::DLLFactoryBase(InspIRCd* Instance, const char* fname, const char* symbol) : DLLManager(Instance, fname) +{ + // try get the factory function if there is no error yet + factory_func = 0; + + if (!LastError()) + { + if (!GetSymbol( (void **)&factory_func, symbol ? symbol : "init_module")) + { + throw ModuleException("Missing init_module() entrypoint!"); + } + } +} + +DLLFactoryBase::~DLLFactoryBase() +{ +} + diff --git a/src/hashcomp.cpp b/src/hashcomp.cpp index e5a6acffe..caf93ec1b 100644 --- a/src/hashcomp.cpp +++ b/src/hashcomp.cpp @@ -1 +1,619 @@ -/* +------------------------------------+
* | 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 "hashcomp.h"
#ifndef WIN32
#include <ext/hash_map>
#define nspace __gnu_cxx
#else
#include <hash_map>
#define nspace stdext
using stdext::hash_map;
#endif
/******************************************************
*
* The hash functions of InspIRCd are the centrepoint
* of the entire system. If these functions are
* inefficient or wasteful, the whole program suffers
* as a result. A lot of C programmers in the ircd
* scene spend a lot of time debating (arguing) about
* the best way to write hash functions to hash irc
* nicknames, channels etc.
* We are lucky as C++ developers as hash_map does
* a lot of this for us. It does intellegent memory
* requests, bucketing, search functions, insertion
* and deletion etc. All we have to do is write some
* overloaded comparison and hash value operators which
* cause it to act in an irc-like way. The features we
* add to the standard hash_map are:
*
* Case insensitivity: The hash_map will be case
* insensitive.
*
* Scandanavian Comparisons: The characters [, ], \ will
* be considered the lowercase of {, } and |.
*
******************************************************/
using namespace irc::sockets;
/* convert a string to lowercase. Note following special circumstances
* taken from RFC 1459. Many "official" server branches still hold to this
* rule so i will too;
*
* Because of IRC's scandanavian origin, the characters {}| are
* considered to be the lower case equivalents of the characters []\,
* respectively. This is a critical issue when determining the
* equivalence of two nicknames.
*/
void nspace::strlower(char *n)
{
if (n)
{
for (char* t = n; *t; t++)
*t = lowermap[(unsigned char)*t];
}
}
#ifndef WIN32
size_t nspace::hash<string>::operator()(const string &s) const
#else
size_t nspace::hash_compare<string, std::less<string> >::operator()(const string &s) const
#endif
{
/* XXX: NO DATA COPIES! :)
* The hash function here is practically
* a copy of the one in STL's hash_fun.h,
* only with *x replaced with lowermap[*x].
* This avoids a copy to use hash<const char*>
*/
register size_t t = 0;
for (std::string::const_iterator x = s.begin(); x != s.end(); ++x) /* ++x not x++, as its faster */
t = 5 * t + lowermap[(unsigned char)*x];
return t;
}
#ifndef WIN32
size_t nspace::hash<irc::string>::operator()(const irc::string &s) const
#else
size_t nspace::hash_compare<irc::string, std::less<irc::string> >::operator()(const irc::string &s) const
#endif
{
register size_t t = 0;
for (irc::string::const_iterator x = s.begin(); x != s.end(); ++x) /* ++x not x++, as its faster */
t = 5 * t + lowermap[(unsigned char)*x];
return t;
}
bool irc::StrHashComp::operator()(const std::string& s1, const std::string& s2) const
{
unsigned char* n1 = (unsigned char*)s1.c_str();
unsigned char* n2 = (unsigned char*)s2.c_str();
for (; *n1 && *n2; n1++, n2++)
if (lowermap[*n1] != lowermap[*n2])
return false;
return (lowermap[*n1] == lowermap[*n2]);
}
/******************************************************
*
* This is the implementation of our special irc::string
* class which is a case-insensitive equivalent to
* std::string which is not only case-insensitive but
* can also do scandanavian comparisons, e.g. { = [, etc.
*
* This class depends on the const array 'lowermap'.
*
******************************************************/
bool irc::irc_char_traits::eq(char c1st, char c2nd)
{
return lowermap[(unsigned char)c1st] == lowermap[(unsigned char)c2nd];
}
bool irc::irc_char_traits::ne(char c1st, char c2nd)
{
return lowermap[(unsigned char)c1st] != lowermap[(unsigned char)c2nd];
}
bool irc::irc_char_traits::lt(char c1st, char c2nd)
{
return lowermap[(unsigned char)c1st] < lowermap[(unsigned char)c2nd];
}
int irc::irc_char_traits::compare(const char* str1, const char* str2, size_t n)
{
for(unsigned int i = 0; i < n; i++)
{
if(lowermap[(unsigned char)*str1] > lowermap[(unsigned char)*str2])
return 1;
if(lowermap[(unsigned char)*str1] < lowermap[(unsigned char)*str2])
return -1;
if(*str1 == 0 || *str2 == 0)
return 0;
str1++;
str2++;
}
return 0;
}
const char* irc::irc_char_traits::find(const char* s1, int n, char c)
{
while(n-- > 0 && lowermap[(unsigned char)*s1] != lowermap[(unsigned char)c])
s1++;
return s1;
}
irc::tokenstream::tokenstream(const std::string &source) : tokens(source), last_pushed(false)
{
/* Record starting position and current position */
last_starting_position = tokens.begin();
n = tokens.begin();
}
irc::tokenstream::~tokenstream()
{
}
bool irc::tokenstream::GetToken(std::string &token)
{
std::string::iterator lsp = last_starting_position;
while (n != tokens.end())
{
/** Skip multi space, converting " " into " "
*/
while ((n+1 != tokens.end()) && (*n == ' ') && (*(n+1) == ' '))
n++;
if ((last_pushed) && (*n == ':'))
{
/* If we find a token thats not the first and starts with :,
* this is the last token on the line
*/
std::string::iterator curr = ++n;
n = tokens.end();
token = std::string(curr, tokens.end());
return true;
}
last_pushed = false;
if ((*n == ' ') || (n+1 == tokens.end()))
{
/* If we find a space, or end of string, this is the end of a token.
*/
last_starting_position = n+1;
last_pushed = true;
std::string strip(lsp, n+1 == tokens.end() ? n+1 : n++);
while ((strip.length()) && (strip.find_last_of(' ') == strip.length() - 1))
strip.erase(strip.end() - 1);
token = strip;
return !token.empty();
}
n++;
}
token.clear();
return false;
}
bool irc::tokenstream::GetToken(irc::string &token)
{
std::string stdstring;
bool returnval = GetToken(stdstring);
token = assign(stdstring);
return returnval;
}
bool irc::tokenstream::GetToken(int &token)
{
std::string tok;
bool returnval = GetToken(tok);
token = ConvToInt(tok);
return returnval;
}
bool irc::tokenstream::GetToken(long &token)
{
std::string tok;
bool returnval = GetToken(tok);
token = ConvToInt(tok);
return returnval;
}
irc::sepstream::sepstream(const std::string &source, char seperator) : tokens(source), sep(seperator)
{
last_starting_position = tokens.begin();
n = tokens.begin();
}
const std::string irc::sepstream::GetToken()
{
std::string::iterator lsp = last_starting_position;
while (n != tokens.end())
{
if ((*n == sep) || (n+1 == tokens.end()))
{
last_starting_position = n+1;
std::string strip = std::string(lsp, n+1 == tokens.end() ? n+1 : n++);
while ((strip.length()) && (strip.find_last_of(sep) == strip.length() - 1))
strip.erase(strip.end() - 1);
return strip;
}
n++;
}
return "";
}
const std::string irc::sepstream::GetRemaining()
{
return std::string(n, tokens.end());
}
bool irc::sepstream::StreamEnd()
{
return ((n + 1) == tokens.end());
}
irc::sepstream::~sepstream()
{
}
std::string irc::hex(const unsigned char *raw, size_t rawsz)
{
if (!rawsz)
return "";
/* EWW! This used to be using sprintf, which is WAY inefficient. -Special */
const char *hex = "0123456789abcdef";
static char hexbuf[MAXBUF];
size_t i, j;
for (i = 0, j = 0; j < rawsz; ++j)
{
hexbuf[i++] = hex[raw[j] / 16];
hexbuf[i++] = hex[raw[j] % 16];
}
hexbuf[i] = 0;
return hexbuf;
}
CoreExport const char* irc::Spacify(const char* n)
{
static char x[MAXBUF];
strlcpy(x,n,MAXBUF);
for (char* y = x; *y; y++)
if (*y == '_')
*y = ' ';
return x;
}
irc::modestacker::modestacker(bool add) : adding(add)
{
sequence.clear();
sequence.push_back("");
}
void irc::modestacker::Push(char modeletter, const std::string ¶meter)
{
*(sequence.begin()) += modeletter;
sequence.push_back(parameter);
}
void irc::modestacker::Push(char modeletter)
{
this->Push(modeletter,"");
}
void irc::modestacker::PushPlus()
{
this->Push('+',"");
}
void irc::modestacker::PushMinus()
{
this->Push('-',"");
}
int irc::modestacker::GetStackedLine(std::deque<std::string> &result, int max_line_size)
{
if (sequence.empty())
{
result.clear();
return 0;
}
int n = 0;
int size = 1; /* Account for initial +/- char */
int nextsize = 0;
result.clear();
result.push_back(adding ? "+" : "-");
if (sequence.size() > 1)
nextsize = sequence[1].length() + 2;
while (!sequence[0].empty() && (sequence.size() > 1) && (result.size() < MAXMODES) && ((size + nextsize) < max_line_size))
{
result[0] += *(sequence[0].begin());
if (!sequence[1].empty())
{
result.push_back(sequence[1]);
size += nextsize; /* Account for mode character and whitespace */
}
sequence[0].erase(sequence[0].begin());
sequence.erase(sequence.begin() + 1);
if (sequence.size() > 1)
nextsize = sequence[1].length() + 2;
n++;
}
return n;
}
irc::stringjoiner::stringjoiner(const std::string &seperator, const std::vector<std::string> &sequence, int begin, int end)
{
for (int v = begin; v < end; v++)
joined.append(sequence[v]).append(seperator);
joined.append(sequence[end]);
}
irc::stringjoiner::stringjoiner(const std::string &seperator, const std::deque<std::string> &sequence, int begin, int end)
{
for (int v = begin; v < end; v++)
joined.append(sequence[v]).append(seperator);
joined.append(sequence[end]);
}
irc::stringjoiner::stringjoiner(const std::string &seperator, const char** sequence, int begin, int end)
{
for (int v = begin; v < end; v++)
joined.append(sequence[v]).append(seperator);
joined.append(sequence[end]);
}
std::string& irc::stringjoiner::GetJoined()
{
return joined;
}
irc::portparser::portparser(const std::string &source, bool allow_overlapped) : in_range(0), range_begin(0), range_end(0), overlapped(allow_overlapped)
{
sep = new irc::commasepstream(source);
overlap_set.clear();
}
irc::portparser::~portparser()
{
delete sep;
}
bool irc::portparser::Overlaps(long val)
{
if (!overlapped)
return false;
if (overlap_set.find(val) == overlap_set.end())
{
overlap_set[val] = true;
return false;
}
else
return true;
}
long irc::portparser::GetToken()
{
if (in_range > 0)
{
in_range++;
if (in_range <= range_end)
{
if (!Overlaps(in_range))
{
return in_range;
}
else
{
while (((Overlaps(in_range)) && (in_range <= range_end)))
in_range++;
if (in_range <= range_end)
return in_range;
}
}
else
in_range = 0;
}
std::string x = sep->GetToken();
if (x.empty())
return 0;
while (Overlaps(atoi(x.c_str())))
{
x = sep->GetToken();
if (x.empty())
return 0;
}
std::string::size_type dash = x.rfind('-');
if (dash != std::string::npos)
{
std::string sbegin = x.substr(0, dash);
std::string send = x.substr(dash+1, x.length());
range_begin = atoi(sbegin.c_str());
range_end = atoi(send.c_str());
if ((range_begin > 0) && (range_end > 0) && (range_begin < 65536) && (range_end < 65536) && (range_begin < range_end))
{
in_range = range_begin;
return in_range;
}
else
{
/* Assume its just the one port */
return atoi(sbegin.c_str());
}
}
else
{
return atoi(x.c_str());
}
}
irc::dynamicbitmask::dynamicbitmask() : bits_size(4)
{
/* We start with 4 bytes allocated which is room
* for 4 items. Something makes me doubt its worth
* allocating less than 4 bytes.
*/
bits = new unsigned char[bits_size];
memset(bits, 0, bits_size);
}
irc::dynamicbitmask::~dynamicbitmask()
{
/* Tidy up the entire used memory on delete */
delete[] bits;
}
irc::bitfield irc::dynamicbitmask::Allocate()
{
/* Yeah, this isnt too efficient, however a module or the core
* should only be allocating bitfields on load, the Toggle and
* Get methods are O(1) as these are called much more often.
*/
unsigned char* freebits = this->GetFreeBits();
for (unsigned char i = 0; i < bits_size; i++)
{
/* Yes, this is right. You'll notice we terminate the loop when !current_pos,
* this is because we logic shift our bit off the end of unsigned char, and its
* lost, making the loop counter 0 when we're done.
*/
for (unsigned char current_pos = 1; current_pos; current_pos = current_pos << 1)
{
if (!(freebits[i] & current_pos))
{
freebits[i] |= current_pos;
return std::make_pair(i, current_pos);
}
}
}
/* We dont have any free space left, increase by one */
if (bits_size == 255)
/* Oh dear, cant grow it any further */
throw std::bad_alloc();
unsigned char old_bits_size = bits_size;
bits_size++;
/* Allocate new bitfield space */
unsigned char* temp_bits = new unsigned char[bits_size];
unsigned char* temp_freebits = new unsigned char[bits_size];
/* Copy the old data in */
memcpy(temp_bits, bits, old_bits_size);
memcpy(temp_freebits, freebits, old_bits_size);
/* Delete the old data pointers */
delete[] bits;
delete[] freebits;
/* Swap the pointers over so now the new
* pointers point to our member values
*/
bits = temp_bits;
freebits = temp_freebits;
this->SetFreeBits(freebits);
/* Initialize the new byte on the end of
* the bitfields, pre-allocate the one bit
* for this allocation
*/
bits[old_bits_size] = 0;
freebits[old_bits_size] = 1;
/* We already know where we just allocated
* the bitfield, so no loop needed
*/
return std::make_pair(old_bits_size, 1);
}
bool irc::dynamicbitmask::Deallocate(irc::bitfield &pos)
{
/* We dont bother to shrink the bitfield
* on deallocation, the most we could do
* is save one byte (!) and this would cost
* us a loop (ugly O(n) stuff) so we just
* clear the bit and leave the memory
* claimed -- nobody will care about one
* byte.
*/
if (pos.first < bits_size)
{
this->GetFreeBits()[pos.first] &= ~pos.second;
return true;
}
/* They gave a bitfield outside of the
* length of our array. BAD programmer.
*/
return false;
}
void irc::dynamicbitmask::Toggle(irc::bitfield &pos, bool state)
{
/* Range check the value */
if (pos.first < bits_size)
{
if (state)
/* Set state, OR the state in */
bits[pos.first] |= pos.second;
else
/* Clear state, AND the !state out */
bits[pos.first] &= ~pos.second;
}
}
bool irc::dynamicbitmask::Get(irc::bitfield &pos)
{
/* Range check the value */
if (pos.first < bits_size)
return (bits[pos.first] & pos.second);
else
/* We can't return false, otherwise we can't
* distinguish between failure and a cleared bit!
* Our only sensible choice is to throw (ew).
*/
throw ModuleException("irc::dynamicbitmask::Get(): Invalid bitfield, out of range");
}
unsigned char irc::dynamicbitmask::GetSize()
{
return bits_size;
}
\ No newline at end of file +/* +------------------------------------+ + * | 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 "hashcomp.h" +#ifndef WIN32 +#include <ext/hash_map> +#define nspace __gnu_cxx +#else +#include <hash_map> +#define nspace stdext +using stdext::hash_map; +#endif + +/****************************************************** + * + * The hash functions of InspIRCd are the centrepoint + * of the entire system. If these functions are + * inefficient or wasteful, the whole program suffers + * as a result. A lot of C programmers in the ircd + * scene spend a lot of time debating (arguing) about + * the best way to write hash functions to hash irc + * nicknames, channels etc. + * We are lucky as C++ developers as hash_map does + * a lot of this for us. It does intellegent memory + * requests, bucketing, search functions, insertion + * and deletion etc. All we have to do is write some + * overloaded comparison and hash value operators which + * cause it to act in an irc-like way. The features we + * add to the standard hash_map are: + * + * Case insensitivity: The hash_map will be case + * insensitive. + * + * Scandanavian Comparisons: The characters [, ], \ will + * be considered the lowercase of {, } and |. + * + ******************************************************/ + +using namespace irc::sockets; + +/* convert a string to lowercase. Note following special circumstances + * taken from RFC 1459. Many "official" server branches still hold to this + * rule so i will too; + * + * Because of IRC's scandanavian origin, the characters {}| are + * considered to be the lower case equivalents of the characters []\, + * respectively. This is a critical issue when determining the + * equivalence of two nicknames. + */ +void nspace::strlower(char *n) +{ + if (n) + { + for (char* t = n; *t; t++) + *t = lowermap[(unsigned char)*t]; + } +} + +#ifndef WIN32 +size_t nspace::hash<string>::operator()(const string &s) const +#else +size_t nspace::hash_compare<string, std::less<string> >::operator()(const string &s) const +#endif +{ + /* XXX: NO DATA COPIES! :) + * The hash function here is practically + * a copy of the one in STL's hash_fun.h, + * only with *x replaced with lowermap[*x]. + * This avoids a copy to use hash<const char*> + */ + register size_t t = 0; + for (std::string::const_iterator x = s.begin(); x != s.end(); ++x) /* ++x not x++, as its faster */ + t = 5 * t + lowermap[(unsigned char)*x]; + return t; +} + +#ifndef WIN32 +size_t nspace::hash<irc::string>::operator()(const irc::string &s) const +#else +size_t nspace::hash_compare<irc::string, std::less<irc::string> >::operator()(const irc::string &s) const +#endif +{ + register size_t t = 0; + for (irc::string::const_iterator x = s.begin(); x != s.end(); ++x) /* ++x not x++, as its faster */ + t = 5 * t + lowermap[(unsigned char)*x]; + return t; +} + +bool irc::StrHashComp::operator()(const std::string& s1, const std::string& s2) const +{ + unsigned char* n1 = (unsigned char*)s1.c_str(); + unsigned char* n2 = (unsigned char*)s2.c_str(); + for (; *n1 && *n2; n1++, n2++) + if (lowermap[*n1] != lowermap[*n2]) + return false; + return (lowermap[*n1] == lowermap[*n2]); +} + +/****************************************************** + * + * This is the implementation of our special irc::string + * class which is a case-insensitive equivalent to + * std::string which is not only case-insensitive but + * can also do scandanavian comparisons, e.g. { = [, etc. + * + * This class depends on the const array 'lowermap'. + * + ******************************************************/ + +bool irc::irc_char_traits::eq(char c1st, char c2nd) +{ + return lowermap[(unsigned char)c1st] == lowermap[(unsigned char)c2nd]; +} + +bool irc::irc_char_traits::ne(char c1st, char c2nd) +{ + return lowermap[(unsigned char)c1st] != lowermap[(unsigned char)c2nd]; +} + +bool irc::irc_char_traits::lt(char c1st, char c2nd) +{ + return lowermap[(unsigned char)c1st] < lowermap[(unsigned char)c2nd]; +} + +int irc::irc_char_traits::compare(const char* str1, const char* str2, size_t n) +{ + for(unsigned int i = 0; i < n; i++) + { + if(lowermap[(unsigned char)*str1] > lowermap[(unsigned char)*str2]) + return 1; + + if(lowermap[(unsigned char)*str1] < lowermap[(unsigned char)*str2]) + return -1; + + if(*str1 == 0 || *str2 == 0) + return 0; + + str1++; + str2++; + } + return 0; +} + +const char* irc::irc_char_traits::find(const char* s1, int n, char c) +{ + while(n-- > 0 && lowermap[(unsigned char)*s1] != lowermap[(unsigned char)c]) + s1++; + return s1; +} + +irc::tokenstream::tokenstream(const std::string &source) : tokens(source), last_pushed(false) +{ + /* Record starting position and current position */ + last_starting_position = tokens.begin(); + n = tokens.begin(); +} + +irc::tokenstream::~tokenstream() +{ +} + +bool irc::tokenstream::GetToken(std::string &token) +{ + std::string::iterator lsp = last_starting_position; + + while (n != tokens.end()) + { + /** Skip multi space, converting " " into " " + */ + while ((n+1 != tokens.end()) && (*n == ' ') && (*(n+1) == ' ')) + n++; + + if ((last_pushed) && (*n == ':')) + { + /* If we find a token thats not the first and starts with :, + * this is the last token on the line + */ + std::string::iterator curr = ++n; + n = tokens.end(); + token = std::string(curr, tokens.end()); + return true; + } + + last_pushed = false; + + if ((*n == ' ') || (n+1 == tokens.end())) + { + /* If we find a space, or end of string, this is the end of a token. + */ + last_starting_position = n+1; + last_pushed = true; + + std::string strip(lsp, n+1 == tokens.end() ? n+1 : n++); + while ((strip.length()) && (strip.find_last_of(' ') == strip.length() - 1)) + strip.erase(strip.end() - 1); + + token = strip; + return !token.empty(); + } + + n++; + } + token.clear(); + return false; +} + +bool irc::tokenstream::GetToken(irc::string &token) +{ + std::string stdstring; + bool returnval = GetToken(stdstring); + token = assign(stdstring); + return returnval; +} + +bool irc::tokenstream::GetToken(int &token) +{ + std::string tok; + bool returnval = GetToken(tok); + token = ConvToInt(tok); + return returnval; +} + +bool irc::tokenstream::GetToken(long &token) +{ + std::string tok; + bool returnval = GetToken(tok); + token = ConvToInt(tok); + return returnval; +} + +irc::sepstream::sepstream(const std::string &source, char seperator) : tokens(source), sep(seperator) +{ + last_starting_position = tokens.begin(); + n = tokens.begin(); +} + +const std::string irc::sepstream::GetToken() +{ + std::string::iterator lsp = last_starting_position; + + while (n != tokens.end()) + { + if ((*n == sep) || (n+1 == tokens.end())) + { + last_starting_position = n+1; + std::string strip = std::string(lsp, n+1 == tokens.end() ? n+1 : n++); + + while ((strip.length()) && (strip.find_last_of(sep) == strip.length() - 1)) + strip.erase(strip.end() - 1); + + return strip; + } + + n++; + } + + return ""; +} + +const std::string irc::sepstream::GetRemaining() +{ + return std::string(n, tokens.end()); +} + +bool irc::sepstream::StreamEnd() +{ + return ((n + 1) == tokens.end()); +} + +irc::sepstream::~sepstream() +{ +} + +std::string irc::hex(const unsigned char *raw, size_t rawsz) +{ + if (!rawsz) + return ""; + + /* EWW! This used to be using sprintf, which is WAY inefficient. -Special */ + + const char *hex = "0123456789abcdef"; + static char hexbuf[MAXBUF]; + + size_t i, j; + for (i = 0, j = 0; j < rawsz; ++j) + { + hexbuf[i++] = hex[raw[j] / 16]; + hexbuf[i++] = hex[raw[j] % 16]; + } + hexbuf[i] = 0; + + return hexbuf; +} + +CoreExport const char* irc::Spacify(const char* n) +{ + static char x[MAXBUF]; + strlcpy(x,n,MAXBUF); + for (char* y = x; *y; y++) + if (*y == '_') + *y = ' '; + return x; +} + + +irc::modestacker::modestacker(bool add) : adding(add) +{ + sequence.clear(); + sequence.push_back(""); +} + +void irc::modestacker::Push(char modeletter, const std::string ¶meter) +{ + *(sequence.begin()) += modeletter; + sequence.push_back(parameter); +} + +void irc::modestacker::Push(char modeletter) +{ + this->Push(modeletter,""); +} + +void irc::modestacker::PushPlus() +{ + this->Push('+',""); +} + +void irc::modestacker::PushMinus() +{ + this->Push('-',""); +} + +int irc::modestacker::GetStackedLine(std::deque<std::string> &result, int max_line_size) +{ + if (sequence.empty()) + { + result.clear(); + return 0; + } + + int n = 0; + int size = 1; /* Account for initial +/- char */ + int nextsize = 0; + result.clear(); + result.push_back(adding ? "+" : "-"); + + if (sequence.size() > 1) + nextsize = sequence[1].length() + 2; + + while (!sequence[0].empty() && (sequence.size() > 1) && (result.size() < MAXMODES) && ((size + nextsize) < max_line_size)) + { + result[0] += *(sequence[0].begin()); + if (!sequence[1].empty()) + { + result.push_back(sequence[1]); + size += nextsize; /* Account for mode character and whitespace */ + } + sequence[0].erase(sequence[0].begin()); + sequence.erase(sequence.begin() + 1); + + if (sequence.size() > 1) + nextsize = sequence[1].length() + 2; + + n++; + } + + return n; +} + +irc::stringjoiner::stringjoiner(const std::string &seperator, const std::vector<std::string> &sequence, int begin, int end) +{ + for (int v = begin; v < end; v++) + joined.append(sequence[v]).append(seperator); + joined.append(sequence[end]); +} + +irc::stringjoiner::stringjoiner(const std::string &seperator, const std::deque<std::string> &sequence, int begin, int end) +{ + for (int v = begin; v < end; v++) + joined.append(sequence[v]).append(seperator); + joined.append(sequence[end]); +} + +irc::stringjoiner::stringjoiner(const std::string &seperator, const char** sequence, int begin, int end) +{ + for (int v = begin; v < end; v++) + joined.append(sequence[v]).append(seperator); + joined.append(sequence[end]); +} + +std::string& irc::stringjoiner::GetJoined() +{ + return joined; +} + +irc::portparser::portparser(const std::string &source, bool allow_overlapped) : in_range(0), range_begin(0), range_end(0), overlapped(allow_overlapped) +{ + sep = new irc::commasepstream(source); + overlap_set.clear(); +} + +irc::portparser::~portparser() +{ + delete sep; +} + +bool irc::portparser::Overlaps(long val) +{ + if (!overlapped) + return false; + + if (overlap_set.find(val) == overlap_set.end()) + { + overlap_set[val] = true; + return false; + } + else + return true; +} + +long irc::portparser::GetToken() +{ + if (in_range > 0) + { + in_range++; + if (in_range <= range_end) + { + if (!Overlaps(in_range)) + { + return in_range; + } + else + { + while (((Overlaps(in_range)) && (in_range <= range_end))) + in_range++; + + if (in_range <= range_end) + return in_range; + } + } + else + in_range = 0; + } + + std::string x = sep->GetToken(); + + if (x.empty()) + return 0; + + while (Overlaps(atoi(x.c_str()))) + { + x = sep->GetToken(); + + if (x.empty()) + return 0; + } + + std::string::size_type dash = x.rfind('-'); + if (dash != std::string::npos) + { + std::string sbegin = x.substr(0, dash); + std::string send = x.substr(dash+1, x.length()); + range_begin = atoi(sbegin.c_str()); + range_end = atoi(send.c_str()); + + if ((range_begin > 0) && (range_end > 0) && (range_begin < 65536) && (range_end < 65536) && (range_begin < range_end)) + { + in_range = range_begin; + return in_range; + } + else + { + /* Assume its just the one port */ + return atoi(sbegin.c_str()); + } + } + else + { + return atoi(x.c_str()); + } +} + +irc::dynamicbitmask::dynamicbitmask() : bits_size(4) +{ + /* We start with 4 bytes allocated which is room + * for 4 items. Something makes me doubt its worth + * allocating less than 4 bytes. + */ + bits = new unsigned char[bits_size]; + memset(bits, 0, bits_size); +} + +irc::dynamicbitmask::~dynamicbitmask() +{ + /* Tidy up the entire used memory on delete */ + delete[] bits; +} + +irc::bitfield irc::dynamicbitmask::Allocate() +{ + /* Yeah, this isnt too efficient, however a module or the core + * should only be allocating bitfields on load, the Toggle and + * Get methods are O(1) as these are called much more often. + */ + unsigned char* freebits = this->GetFreeBits(); + for (unsigned char i = 0; i < bits_size; i++) + { + /* Yes, this is right. You'll notice we terminate the loop when !current_pos, + * this is because we logic shift our bit off the end of unsigned char, and its + * lost, making the loop counter 0 when we're done. + */ + for (unsigned char current_pos = 1; current_pos; current_pos = current_pos << 1) + { + if (!(freebits[i] & current_pos)) + { + freebits[i] |= current_pos; + return std::make_pair(i, current_pos); + } + } + } + /* We dont have any free space left, increase by one */ + + if (bits_size == 255) + /* Oh dear, cant grow it any further */ + throw std::bad_alloc(); + + unsigned char old_bits_size = bits_size; + bits_size++; + /* Allocate new bitfield space */ + unsigned char* temp_bits = new unsigned char[bits_size]; + unsigned char* temp_freebits = new unsigned char[bits_size]; + /* Copy the old data in */ + memcpy(temp_bits, bits, old_bits_size); + memcpy(temp_freebits, freebits, old_bits_size); + /* Delete the old data pointers */ + delete[] bits; + delete[] freebits; + /* Swap the pointers over so now the new + * pointers point to our member values + */ + bits = temp_bits; + freebits = temp_freebits; + this->SetFreeBits(freebits); + /* Initialize the new byte on the end of + * the bitfields, pre-allocate the one bit + * for this allocation + */ + bits[old_bits_size] = 0; + freebits[old_bits_size] = 1; + /* We already know where we just allocated + * the bitfield, so no loop needed + */ + return std::make_pair(old_bits_size, 1); +} + +bool irc::dynamicbitmask::Deallocate(irc::bitfield &pos) +{ + /* We dont bother to shrink the bitfield + * on deallocation, the most we could do + * is save one byte (!) and this would cost + * us a loop (ugly O(n) stuff) so we just + * clear the bit and leave the memory + * claimed -- nobody will care about one + * byte. + */ + if (pos.first < bits_size) + { + this->GetFreeBits()[pos.first] &= ~pos.second; + return true; + } + /* They gave a bitfield outside of the + * length of our array. BAD programmer. + */ + return false; +} + +void irc::dynamicbitmask::Toggle(irc::bitfield &pos, bool state) +{ + /* Range check the value */ + if (pos.first < bits_size) + { + if (state) + /* Set state, OR the state in */ + bits[pos.first] |= pos.second; + else + /* Clear state, AND the !state out */ + bits[pos.first] &= ~pos.second; + } +} + +bool irc::dynamicbitmask::Get(irc::bitfield &pos) +{ + /* Range check the value */ + if (pos.first < bits_size) + return (bits[pos.first] & pos.second); + else + /* We can't return false, otherwise we can't + * distinguish between failure and a cleared bit! + * Our only sensible choice is to throw (ew). + */ + throw ModuleException("irc::dynamicbitmask::Get(): Invalid bitfield, out of range"); +} + +unsigned char irc::dynamicbitmask::GetSize() +{ + return bits_size; +} + diff --git a/src/helperfuncs.cpp b/src/helperfuncs.cpp index b5d8f5630..da3fa5e1b 100644 --- a/src/helperfuncs.cpp +++ b/src/helperfuncs.cpp @@ -1 +1,534 @@ -/* +------------------------------------+
* | 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 <stdarg.h>
#include "configreader.h"
#include "users.h"
#include "modules.h"
#include "wildcard.h"
#include "mode.h"
#include "xline.h"
#include "exitcodes.h"
static char TIMESTR[26];
static time_t LAST = 0;
/** Log()
* Write a line of text `text' to the logfile (and stdout, if in nofork) if the level `level'
* is greater than the configured loglevel.
*/
void InspIRCd::Log(int level, const char* text, ...)
{
/* Do this check again here so that we save pointless vsnprintf calls */
if ((level < Config->LogLevel) && !Config->forcedebug)
return;
va_list argsPtr;
char textbuffer[65536];
va_start(argsPtr, text);
vsnprintf(textbuffer, 65536, text, argsPtr);
va_end(argsPtr);
this->Log(level, std::string(textbuffer));
}
void InspIRCd::Log(int level, const std::string &text)
{
if (!this->Config)
return;
/* If we were given -debug we output all messages, regardless of configured loglevel */
if ((level < Config->LogLevel) && !Config->forcedebug)
return;
if (Time() != LAST)
{
time_t local = Time();
struct tm *timeinfo = localtime(&local);
strlcpy(TIMESTR,asctime(timeinfo),26);
TIMESTR[24] = ':';
LAST = Time();
}
if (Config->log_file && Config->writelog)
{
std::string out = std::string(TIMESTR) + " " + text.c_str() + "\n";
this->Logger->WriteLogLine(out);
}
if (Config->nofork)
{
printf("%s %s\n", TIMESTR, text.c_str());
}
}
std::string InspIRCd::GetServerDescription(const char* servername)
{
std::string description;
FOREACH_MOD_I(this,I_OnGetServerDescription,OnGetServerDescription(servername,description));
if (!description.empty())
{
return description;
}
else
{
// not a remote server that can be found, it must be me.
return Config->ServerDesc;
}
}
/* XXX - We don't use WriteMode for this because WriteMode is very slow and
* this isnt. Basically WriteMode has to iterate ALL the users 'n' times for
* the number of modes provided, e.g. if you send WriteMode 'og' to write to
* opers with globops, and you have 2000 users, thats 4000 iterations. WriteOpers
* uses the oper list, which means if you have 2000 users but only 5 opers,
* it iterates 5 times.
*/
void InspIRCd::WriteOpers(const char* text, ...)
{
char textbuffer[MAXBUF];
va_list argsPtr;
va_start(argsPtr, text);
vsnprintf(textbuffer, MAXBUF, text, argsPtr);
va_end(argsPtr);
this->WriteOpers(std::string(textbuffer));
}
void InspIRCd::WriteOpers(const std::string &text)
{
for (std::vector<userrec*>::iterator i = this->all_opers.begin(); i != this->all_opers.end(); i++)
{
userrec* a = *i;
if (IS_LOCAL(a) && a->modes[UM_SERVERNOTICE])
{
// send server notices to all with +s
a->WriteServ("NOTICE %s :%s",a->nick,text.c_str());
}
}
}
void InspIRCd::ServerNoticeAll(char* text, ...)
{
if (!text)
return;
char textbuffer[MAXBUF];
char formatbuffer[MAXBUF];
va_list argsPtr;
va_start (argsPtr, text);
vsnprintf(textbuffer, MAXBUF, text, argsPtr);
va_end(argsPtr);
snprintf(formatbuffer,MAXBUF,"NOTICE $%s :%s",Config->ServerName,textbuffer);
for (std::vector<userrec*>::const_iterator i = local_users.begin(); i != local_users.end(); i++)
{
userrec* t = *i;
t->WriteServ(std::string(formatbuffer));
}
}
void InspIRCd::ServerPrivmsgAll(char* text, ...)
{
if (!text)
return;
char textbuffer[MAXBUF];
char formatbuffer[MAXBUF];
va_list argsPtr;
va_start (argsPtr, text);
vsnprintf(textbuffer, MAXBUF, text, argsPtr);
va_end(argsPtr);
snprintf(formatbuffer,MAXBUF,"PRIVMSG $%s :%s",Config->ServerName,textbuffer);
for (std::vector<userrec*>::const_iterator i = local_users.begin(); i != local_users.end(); i++)
{
userrec* t = *i;
t->WriteServ(std::string(formatbuffer));
}
}
void InspIRCd::WriteMode(const char* modes, int flags, const char* text, ...)
{
char textbuffer[MAXBUF];
int modelen;
va_list argsPtr;
if (!text || !modes || !flags)
{
this->Log(DEFAULT,"*** BUG *** WriteMode was given an invalid parameter");
return;
}
va_start(argsPtr, text);
vsnprintf(textbuffer, MAXBUF, text, argsPtr);
va_end(argsPtr);
modelen = strlen(modes);
if (flags == WM_AND)
{
for (std::vector<userrec*>::const_iterator i = local_users.begin(); i != local_users.end(); i++)
{
userrec* t = *i;
bool send_to_user = true;
for (int n = 0; n < modelen; n++)
{
if (!t->modes[modes[n]-65])
{
send_to_user = false;
break;
}
}
if (send_to_user)
t->WriteServ("NOTICE %s :%s",t->nick,textbuffer);
}
}
else
if (flags == WM_OR)
{
for (std::vector<userrec*>::const_iterator i = local_users.begin(); i != local_users.end(); i++)
{
userrec* t = *i;
bool send_to_user = false;
for (int n = 0; n < modelen; n++)
{
if (t->modes[modes[n]-65])
{
send_to_user = true;
break;
}
}
if (send_to_user)
t->WriteServ("NOTICE %s :%s",t->nick,textbuffer);
}
}
}
/* Find a user record by nickname and return a pointer to it */
userrec* InspIRCd::FindNick(const std::string &nick)
{
user_hash::iterator iter = clientlist->find(nick);
if (iter == clientlist->end())
/* Couldn't find it */
return NULL;
return iter->second;
}
userrec* InspIRCd::FindNick(const char* nick)
{
user_hash::iterator iter = clientlist->find(nick);
if (iter == clientlist->end())
return NULL;
return iter->second;
}
/* find a channel record by channel name and return a pointer to it */
chanrec* InspIRCd::FindChan(const char* chan)
{
chan_hash::iterator iter = chanlist->find(chan);
if (iter == chanlist->end())
/* Couldn't find it */
return NULL;
return iter->second;
}
chanrec* InspIRCd::FindChan(const std::string &chan)
{
chan_hash::iterator iter = chanlist->find(chan);
if (iter == chanlist->end())
/* Couldn't find it */
return NULL;
return iter->second;
}
/*
* sends out an error notice to all connected clients (not to be used
* lightly!)
*/
void InspIRCd::SendError(const std::string &s)
{
for (std::vector<userrec*>::const_iterator i = this->local_users.begin(); i != this->local_users.end(); i++)
{
if ((*i)->registered == REG_ALL)
{
(*i)->WriteServ("NOTICE %s :%s",(*i)->nick,s.c_str());
}
else
{
/* Unregistered connections receive ERROR, not a NOTICE */
(*i)->Write("ERROR :" + s);
}
/* This might generate a whole load of EAGAIN, but we dont really
* care about this, as if we call SendError something catastrophic
* has occured anyway, and we wont receive the events for these.
*/
(*i)->FlushWriteBuf();
}
}
// this function counts all users connected, wether they are registered or NOT.
int InspIRCd::UserCount()
{
return clientlist->size();
}
// this counts only registered users, so that the percentages in /MAP don't mess up when users are sitting in an unregistered state
int InspIRCd::RegisteredUserCount()
{
return clientlist->size() - this->UnregisteredUserCount();
}
int InspIRCd::ModeCount(const char mode)
{
ModeHandler* mh = this->Modes->FindMode(mode, MODETYPE_USER);
if (mh)
return mh->GetCount();
else
return 0;
}
int InspIRCd::InvisibleUserCount()
{
return ModeCount('i');
}
int InspIRCd::OperCount()
{
return this->all_opers.size();
}
int InspIRCd::UnregisteredUserCount()
{
return this->unregistered_count;
}
long InspIRCd::ChannelCount()
{
return chanlist->size();
}
long InspIRCd::LocalUserCount()
{
/* Doesnt count unregistered clients */
return (local_users.size() - this->UnregisteredUserCount());
}
bool InspIRCd::IsChannel(const char *chname)
{
char *c;
/* check for no name - don't check for !*chname, as if it is empty, it won't be '#'! */
if (!chname || *chname != '#')
{
return false;
}
c = (char *)chname + 1;
while (*c)
{
switch (*c)
{
case ' ':
case ',':
case 7:
return false;
}
c++;
}
/* too long a name - note funky pointer arithmetic here. */
if ((c - chname) > CHANMAX)
{
return false;
}
return true;
}
bool InspIRCd::IsNick(const char* n)
{
if (!n || !*n)
return false;
int p = 0;
for (char* i = (char*)n; *i; i++, p++)
{
if ((*i >= 'A') && (*i <= '}'))
{
/* "A"-"}" can occur anywhere in a nickname */
continue;
}
if ((((*i >= '0') && (*i <= '9')) || (*i == '-')) && (i > n))
{
/* "0"-"9", "-" can occur anywhere BUT the first char of a nickname */
continue;
}
/* invalid character! abort */
return false;
}
/* too long? or not -- pointer arithmetic rocks */
return (p < NICKMAX - 1);
}
bool InspIRCd::IsIdent(const char* n)
{
if (!n || !*n)
return false;
for (char* i = (char*)n; *i; i++)
{
if ((*i >= 'A') && (*i <= '}'))
{
continue;
}
if (((*i >= '0') && (*i <= '9')) || (*i == '-') || (*i == '.'))
{
continue;
}
return false;
}
return true;
}
void InspIRCd::OpenLog(char** argv, int argc)
{
Config->MyDir = Config->GetFullProgDir();
if (!*this->LogFileName)
{
if (Config->logpath.empty())
{
#ifndef DARWIN
Config->logpath = Config->MyDir + "/ircd.log";
#else
Config->logpath = "/var/log/ircd.log";
#endif
}
Config->log_file = fopen(Config->logpath.c_str(),"a+");
}
else
{
Config->log_file = fopen(this->LogFileName,"a+");
}
if (!Config->log_file)
{
printf("ERROR: Could not write to logfile %s: %s\n\n", Config->logpath.c_str(), strerror(errno));
Exit(EXIT_STATUS_LOG);
}
this->Logger = new FileLogger(this, Config->log_file);
}
void InspIRCd::CheckRoot()
{
#ifndef DARWIN
if (geteuid() == 0)
{
printf("WARNING!!! You are running an irc server as ROOT!!! DO NOT DO THIS!!!\n\n");
this->Log(DEFAULT,"Cant start as root");
#else
if (geteuid() != 16)
{
printf("WARNING!!! You are not running inspircd as the ircdaemon user!!! YOU CAN NOT DO THIS!!!\n\n");
this->Log(DEFAULT,"Must start as user ircdaemon");
#endif
Exit(EXIT_STATUS_ROOT);
}
}
void InspIRCd::CheckDie()
{
if (*Config->DieValue)
{
printf("WARNING: %s\n\n",Config->DieValue);
this->Log(DEFAULT,"Died because of <die> tag: %s",Config->DieValue);
Exit(EXIT_STATUS_DIETAG);
}
}
/* We must load the modules AFTER initializing the socket engine, now */
void InspIRCd::LoadAllModules()
{
char configToken[MAXBUF];
Config->module_names.clear();
this->ModCount = -1;
for (int count = 0; count < Config->ConfValueEnum(Config->config_data, "module"); count++)
{
Config->ConfValue(Config->config_data, "module", "name", count, configToken, MAXBUF);
printf_c("[\033[1;32m*\033[0m] Loading module:\t\033[1;32m%s\033[0m\n",configToken);
if (!this->LoadModule(configToken))
{
this->Log(DEFAULT,"There was an error loading the module '%s': %s", configToken, this->ModuleError());
printf_c("\n[\033[1;31m*\033[0m] There was an error loading the module '%s': %s\n\n", configToken, this->ModuleError());
Exit(EXIT_STATUS_MODULE);
}
}
printf_c("\nA total of \033[1;32m%d\033[0m module%s been loaded.\n", this->ModCount+1, this->ModCount+1 == 1 ? " has" : "s have");
this->Log(DEFAULT,"Total loaded modules: %d", this->ModCount+1);
}
void InspIRCd::SendWhoisLine(userrec* user, userrec* dest, int numeric, const std::string &text)
{
std::string copy_text = text;
int MOD_RESULT = 0;
FOREACH_RESULT_I(this, I_OnWhoisLine, OnWhoisLine(user, dest, numeric, copy_text));
if (!MOD_RESULT)
user->WriteServ("%d %s", numeric, copy_text.c_str());
}
void InspIRCd::SendWhoisLine(userrec* user, userrec* dest, int numeric, const char* format, ...)
{
char textbuffer[MAXBUF];
va_list argsPtr;
va_start (argsPtr, format);
vsnprintf(textbuffer, MAXBUF, format, argsPtr);
va_end(argsPtr);
this->SendWhoisLine(user, dest, numeric, std::string(textbuffer));
}
\ No newline at end of file +/* +------------------------------------+ + * | 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 <stdarg.h> +#include "configreader.h" +#include "users.h" +#include "modules.h" +#include "wildcard.h" +#include "mode.h" +#include "xline.h" +#include "exitcodes.h" + +static char TIMESTR[26]; +static time_t LAST = 0; + +/** Log() + * Write a line of text `text' to the logfile (and stdout, if in nofork) if the level `level' + * is greater than the configured loglevel. + */ +void InspIRCd::Log(int level, const char* text, ...) +{ + /* Do this check again here so that we save pointless vsnprintf calls */ + if ((level < Config->LogLevel) && !Config->forcedebug) + return; + + va_list argsPtr; + char textbuffer[65536]; + + va_start(argsPtr, text); + vsnprintf(textbuffer, 65536, text, argsPtr); + va_end(argsPtr); + + this->Log(level, std::string(textbuffer)); +} + +void InspIRCd::Log(int level, const std::string &text) +{ + if (!this->Config) + return; + + /* If we were given -debug we output all messages, regardless of configured loglevel */ + if ((level < Config->LogLevel) && !Config->forcedebug) + return; + + if (Time() != LAST) + { + time_t local = Time(); + struct tm *timeinfo = localtime(&local); + + strlcpy(TIMESTR,asctime(timeinfo),26); + TIMESTR[24] = ':'; + LAST = Time(); + } + + if (Config->log_file && Config->writelog) + { + std::string out = std::string(TIMESTR) + " " + text.c_str() + "\n"; + this->Logger->WriteLogLine(out); + } + + if (Config->nofork) + { + printf("%s %s\n", TIMESTR, text.c_str()); + } +} + +std::string InspIRCd::GetServerDescription(const char* servername) +{ + std::string description; + + FOREACH_MOD_I(this,I_OnGetServerDescription,OnGetServerDescription(servername,description)); + + if (!description.empty()) + { + return description; + } + else + { + // not a remote server that can be found, it must be me. + return Config->ServerDesc; + } +} + +/* XXX - We don't use WriteMode for this because WriteMode is very slow and + * this isnt. Basically WriteMode has to iterate ALL the users 'n' times for + * the number of modes provided, e.g. if you send WriteMode 'og' to write to + * opers with globops, and you have 2000 users, thats 4000 iterations. WriteOpers + * uses the oper list, which means if you have 2000 users but only 5 opers, + * it iterates 5 times. + */ +void InspIRCd::WriteOpers(const char* text, ...) +{ + char textbuffer[MAXBUF]; + va_list argsPtr; + + va_start(argsPtr, text); + vsnprintf(textbuffer, MAXBUF, text, argsPtr); + va_end(argsPtr); + + this->WriteOpers(std::string(textbuffer)); +} + +void InspIRCd::WriteOpers(const std::string &text) +{ + for (std::vector<userrec*>::iterator i = this->all_opers.begin(); i != this->all_opers.end(); i++) + { + userrec* a = *i; + if (IS_LOCAL(a) && a->modes[UM_SERVERNOTICE]) + { + // send server notices to all with +s + a->WriteServ("NOTICE %s :%s",a->nick,text.c_str()); + } + } +} + +void InspIRCd::ServerNoticeAll(char* text, ...) +{ + if (!text) + return; + + char textbuffer[MAXBUF]; + char formatbuffer[MAXBUF]; + va_list argsPtr; + va_start (argsPtr, text); + vsnprintf(textbuffer, MAXBUF, text, argsPtr); + va_end(argsPtr); + + snprintf(formatbuffer,MAXBUF,"NOTICE $%s :%s",Config->ServerName,textbuffer); + + for (std::vector<userrec*>::const_iterator i = local_users.begin(); i != local_users.end(); i++) + { + userrec* t = *i; + t->WriteServ(std::string(formatbuffer)); + } +} + +void InspIRCd::ServerPrivmsgAll(char* text, ...) +{ + if (!text) + return; + + char textbuffer[MAXBUF]; + char formatbuffer[MAXBUF]; + va_list argsPtr; + va_start (argsPtr, text); + vsnprintf(textbuffer, MAXBUF, text, argsPtr); + va_end(argsPtr); + + snprintf(formatbuffer,MAXBUF,"PRIVMSG $%s :%s",Config->ServerName,textbuffer); + + for (std::vector<userrec*>::const_iterator i = local_users.begin(); i != local_users.end(); i++) + { + userrec* t = *i; + t->WriteServ(std::string(formatbuffer)); + } +} + +void InspIRCd::WriteMode(const char* modes, int flags, const char* text, ...) +{ + char textbuffer[MAXBUF]; + int modelen; + va_list argsPtr; + + if (!text || !modes || !flags) + { + this->Log(DEFAULT,"*** BUG *** WriteMode was given an invalid parameter"); + return; + } + + va_start(argsPtr, text); + vsnprintf(textbuffer, MAXBUF, text, argsPtr); + va_end(argsPtr); + modelen = strlen(modes); + + if (flags == WM_AND) + { + for (std::vector<userrec*>::const_iterator i = local_users.begin(); i != local_users.end(); i++) + { + userrec* t = *i; + bool send_to_user = true; + + for (int n = 0; n < modelen; n++) + { + if (!t->modes[modes[n]-65]) + { + send_to_user = false; + break; + } + } + if (send_to_user) + t->WriteServ("NOTICE %s :%s",t->nick,textbuffer); + } + } + else + if (flags == WM_OR) + { + for (std::vector<userrec*>::const_iterator i = local_users.begin(); i != local_users.end(); i++) + { + userrec* t = *i; + bool send_to_user = false; + + for (int n = 0; n < modelen; n++) + { + if (t->modes[modes[n]-65]) + { + send_to_user = true; + break; + } + } + if (send_to_user) + t->WriteServ("NOTICE %s :%s",t->nick,textbuffer); + } + } +} + +/* Find a user record by nickname and return a pointer to it */ + +userrec* InspIRCd::FindNick(const std::string &nick) +{ + user_hash::iterator iter = clientlist->find(nick); + + if (iter == clientlist->end()) + /* Couldn't find it */ + return NULL; + + return iter->second; +} + +userrec* InspIRCd::FindNick(const char* nick) +{ + user_hash::iterator iter = clientlist->find(nick); + + if (iter == clientlist->end()) + return NULL; + + return iter->second; +} + +/* find a channel record by channel name and return a pointer to it */ + +chanrec* InspIRCd::FindChan(const char* chan) +{ + chan_hash::iterator iter = chanlist->find(chan); + + if (iter == chanlist->end()) + /* Couldn't find it */ + return NULL; + + return iter->second; +} + +chanrec* InspIRCd::FindChan(const std::string &chan) +{ + chan_hash::iterator iter = chanlist->find(chan); + + if (iter == chanlist->end()) + /* Couldn't find it */ + return NULL; + + return iter->second; +} + +/* + * sends out an error notice to all connected clients (not to be used + * lightly!) + */ +void InspIRCd::SendError(const std::string &s) +{ + for (std::vector<userrec*>::const_iterator i = this->local_users.begin(); i != this->local_users.end(); i++) + { + if ((*i)->registered == REG_ALL) + { + (*i)->WriteServ("NOTICE %s :%s",(*i)->nick,s.c_str()); + } + else + { + /* Unregistered connections receive ERROR, not a NOTICE */ + (*i)->Write("ERROR :" + s); + } + /* This might generate a whole load of EAGAIN, but we dont really + * care about this, as if we call SendError something catastrophic + * has occured anyway, and we wont receive the events for these. + */ + (*i)->FlushWriteBuf(); + } +} + +// this function counts all users connected, wether they are registered or NOT. +int InspIRCd::UserCount() +{ + return clientlist->size(); +} + +// this counts only registered users, so that the percentages in /MAP don't mess up when users are sitting in an unregistered state +int InspIRCd::RegisteredUserCount() +{ + return clientlist->size() - this->UnregisteredUserCount(); +} + +int InspIRCd::ModeCount(const char mode) +{ + ModeHandler* mh = this->Modes->FindMode(mode, MODETYPE_USER); + + if (mh) + return mh->GetCount(); + else + return 0; +} + +int InspIRCd::InvisibleUserCount() +{ + return ModeCount('i'); +} + +int InspIRCd::OperCount() +{ + return this->all_opers.size(); +} + +int InspIRCd::UnregisteredUserCount() +{ + return this->unregistered_count; +} + +long InspIRCd::ChannelCount() +{ + return chanlist->size(); +} + +long InspIRCd::LocalUserCount() +{ + /* Doesnt count unregistered clients */ + return (local_users.size() - this->UnregisteredUserCount()); +} + +bool InspIRCd::IsChannel(const char *chname) +{ + char *c; + + /* check for no name - don't check for !*chname, as if it is empty, it won't be '#'! */ + if (!chname || *chname != '#') + { + return false; + } + + c = (char *)chname + 1; + while (*c) + { + switch (*c) + { + case ' ': + case ',': + case 7: + return false; + } + + c++; + } + + /* too long a name - note funky pointer arithmetic here. */ + if ((c - chname) > CHANMAX) + { + return false; + } + + return true; +} + +bool InspIRCd::IsNick(const char* n) +{ + if (!n || !*n) + return false; + + int p = 0; + for (char* i = (char*)n; *i; i++, p++) + { + if ((*i >= 'A') && (*i <= '}')) + { + /* "A"-"}" can occur anywhere in a nickname */ + continue; + } + + if ((((*i >= '0') && (*i <= '9')) || (*i == '-')) && (i > n)) + { + /* "0"-"9", "-" can occur anywhere BUT the first char of a nickname */ + continue; + } + + /* invalid character! abort */ + return false; + } + + /* too long? or not -- pointer arithmetic rocks */ + return (p < NICKMAX - 1); +} + + +bool InspIRCd::IsIdent(const char* n) +{ + if (!n || !*n) + return false; + + for (char* i = (char*)n; *i; i++) + { + if ((*i >= 'A') && (*i <= '}')) + { + continue; + } + + if (((*i >= '0') && (*i <= '9')) || (*i == '-') || (*i == '.')) + { + continue; + } + + return false; + } + + return true; +} + +void InspIRCd::OpenLog(char** argv, int argc) +{ + Config->MyDir = Config->GetFullProgDir(); + + if (!*this->LogFileName) + { + if (Config->logpath.empty()) + { +#ifndef DARWIN + Config->logpath = Config->MyDir + "/ircd.log"; +#else + Config->logpath = "/var/log/ircd.log"; +#endif + } + + Config->log_file = fopen(Config->logpath.c_str(),"a+"); + } + else + { + Config->log_file = fopen(this->LogFileName,"a+"); + } + + if (!Config->log_file) + { + printf("ERROR: Could not write to logfile %s: %s\n\n", Config->logpath.c_str(), strerror(errno)); + Exit(EXIT_STATUS_LOG); + } + + this->Logger = new FileLogger(this, Config->log_file); +} + +void InspIRCd::CheckRoot() +{ +#ifndef DARWIN + if (geteuid() == 0) + { + printf("WARNING!!! You are running an irc server as ROOT!!! DO NOT DO THIS!!!\n\n"); + this->Log(DEFAULT,"Cant start as root"); +#else + if (geteuid() != 16) + { + printf("WARNING!!! You are not running inspircd as the ircdaemon user!!! YOU CAN NOT DO THIS!!!\n\n"); + this->Log(DEFAULT,"Must start as user ircdaemon"); +#endif + Exit(EXIT_STATUS_ROOT); + } +} + +void InspIRCd::CheckDie() +{ + if (*Config->DieValue) + { + printf("WARNING: %s\n\n",Config->DieValue); + this->Log(DEFAULT,"Died because of <die> tag: %s",Config->DieValue); + Exit(EXIT_STATUS_DIETAG); + } +} + +/* We must load the modules AFTER initializing the socket engine, now */ +void InspIRCd::LoadAllModules() +{ + char configToken[MAXBUF]; + Config->module_names.clear(); + this->ModCount = -1; + + for (int count = 0; count < Config->ConfValueEnum(Config->config_data, "module"); count++) + { + Config->ConfValue(Config->config_data, "module", "name", count, configToken, MAXBUF); + printf_c("[\033[1;32m*\033[0m] Loading module:\t\033[1;32m%s\033[0m\n",configToken); + + if (!this->LoadModule(configToken)) + { + this->Log(DEFAULT,"There was an error loading the module '%s': %s", configToken, this->ModuleError()); + printf_c("\n[\033[1;31m*\033[0m] There was an error loading the module '%s': %s\n\n", configToken, this->ModuleError()); + Exit(EXIT_STATUS_MODULE); + } + } + printf_c("\nA total of \033[1;32m%d\033[0m module%s been loaded.\n", this->ModCount+1, this->ModCount+1 == 1 ? " has" : "s have"); + this->Log(DEFAULT,"Total loaded modules: %d", this->ModCount+1); +} + +void InspIRCd::SendWhoisLine(userrec* user, userrec* dest, int numeric, const std::string &text) +{ + std::string copy_text = text; + + int MOD_RESULT = 0; + FOREACH_RESULT_I(this, I_OnWhoisLine, OnWhoisLine(user, dest, numeric, copy_text)); + + if (!MOD_RESULT) + user->WriteServ("%d %s", numeric, copy_text.c_str()); +} + +void InspIRCd::SendWhoisLine(userrec* user, userrec* dest, int numeric, const char* format, ...) +{ + char textbuffer[MAXBUF]; + va_list argsPtr; + va_start (argsPtr, format); + vsnprintf(textbuffer, MAXBUF, format, argsPtr); + va_end(argsPtr); + + this->SendWhoisLine(user, dest, numeric, std::string(textbuffer)); +} + diff --git a/src/inspircd.cpp b/src/inspircd.cpp index 7f9342706..858862e9d 100644 --- a/src/inspircd.cpp +++ b/src/inspircd.cpp @@ -1 +1,1307 @@ -/* +------------------------------------+
* | 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 <signal.h>
#ifndef WIN32
#include <dirent.h>
#include <unistd.h>
#include <sys/resource.h>
#include <dlfcn.h>
#include <getopt.h>
/* Some systems don't define RUSAGE_SELF. This should fix them. */
#ifndef RUSAGE_SELF
#define RUSAGE_SELF 0
#endif
#endif
#include <exception>
#include <fstream>
#include "modules.h"
#include "mode.h"
#include "xline.h"
#include "socketengine.h"
#include "inspircd_se_config.h"
#include "socket.h"
#include "typedefs.h"
#include "command_parse.h"
#include "exitcodes.h"
#ifdef WIN32
/* This MUST remain static and delcared outside the class, so that WriteProcessMemory can reference it properly */
static DWORD owner_processid = 0;
DWORD WindowsForkStart(InspIRCd * Instance)
{
/* Windows implementation of fork() :P */
char module[MAX_PATH];
if(!GetModuleFileName(NULL, module, MAX_PATH))
{
printf("GetModuleFileName() failed.\n");
return false;
}
STARTUPINFO startupinfo;
PROCESS_INFORMATION procinfo;
ZeroMemory(&startupinfo, sizeof(STARTUPINFO));
ZeroMemory(&procinfo, sizeof(PROCESS_INFORMATION));
// Fill in the startup info struct
GetStartupInfo(&startupinfo);
/* Default creation flags create the processes suspended */
DWORD startupflags = CREATE_SUSPENDED;
/* On windows 2003/XP and above, we can use the value
* CREATE_PRESERVE_CODE_AUTHZ_LEVEL which gives more access
* to the process which we may require on these operating systems.
*/
OSVERSIONINFO vi;
vi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
GetVersionEx(&vi);
if ((vi.dwMajorVersion >= 5) && (vi.dwMinorVersion > 0))
startupflags |= CREATE_PRESERVE_CODE_AUTHZ_LEVEL;
// Launch our "forked" process.
BOOL bSuccess = CreateProcess ( module, // Module (exe) filename
strdup(GetCommandLine()), // Command line (exe plus parameters from the OS)
// NOTE: We cannot return the direct value of the
// GetCommandLine function here, as the pointer is
// passed straight to the child process, and will be
// invalid once we exit as it goes out of context.
// strdup() seems ok, though.
0, // PROCESS_SECURITY_ATTRIBUTES
0, // THREAD_SECURITY_ATTRIBUTES
TRUE, // We went to inherit handles.
startupflags, // Allow us full access to the process and suspend it.
0, // ENVIRONMENT
0, // CURRENT_DIRECTORY
&startupinfo, // startup info
&procinfo); // process info
if(!bSuccess)
{
printf("CreateProcess() error: %s\n", dlerror());
return false;
}
// Set the owner process id in the target process.
SIZE_T written = 0;
DWORD pid = GetCurrentProcessId();
if(!WriteProcessMemory(procinfo.hProcess, &owner_processid, &pid, sizeof(DWORD), &written) || written != sizeof(DWORD))
{
printf("WriteProcessMemory() failed: %s\n", dlerror());
return false;
}
// Resume the other thread (let it start)
ResumeThread(procinfo.hThread);
// Wait for the new process to kill us. If there is some error, the new process will end and we will end up at the next line.
WaitForSingleObject(procinfo.hProcess, INFINITE);
// If we hit this it means startup failed, default to 14 if this fails.
DWORD ExitCode = 14;
GetExitCodeProcess(procinfo.hProcess, &ExitCode);
CloseHandle(procinfo.hThread);
CloseHandle(procinfo.hProcess);
return ExitCode;
}
void WindowsForkKillOwner(InspIRCd * Instance)
{
HANDLE hProcess = OpenProcess(PROCESS_TERMINATE, FALSE, owner_processid);
if(!hProcess || !owner_processid)
{
printf("Could not open process id %u: %s.\n", owner_processid, dlerror());
Instance->Exit(14);
}
// die die die
if(!TerminateProcess(hProcess, 0))
{
printf("Could not TerminateProcess(): %s\n", dlerror());
Instance->Exit(14);
}
CloseHandle(hProcess);
}
#endif
using irc::sockets::NonBlocking;
using irc::sockets::Blocking;
using irc::sockets::insp_ntoa;
using irc::sockets::insp_inaddr;
using irc::sockets::insp_sockaddr;
InspIRCd* SI = NULL;
/* Burlex: Moved from exitcodes.h -- due to duplicate symbols */
const char* ExitCodes[] =
{
"No error", /* 0 */
"DIE command", /* 1 */
"execv() failed", /* 2 */
"Internal error", /* 3 */
"Config file error", /* 4 */
"Logfile error", /* 5 */
"POSIX fork failed", /* 6 */
"Bad commandline parameters", /* 7 */
"No ports could be bound", /* 8 */
"Can't write PID file", /* 9 */
"SocketEngine could not initialize", /* 10 */
"Refusing to start up as root", /* 11 */
"Found a <die> tag!", /* 12 */
"Couldn't load module on startup", /* 13 */
"Could not create windows forked process", /* 14 */
"Received SIGTERM", /* 15 */
};
void InspIRCd::AddServerName(const std::string &servername)
{
servernamelist::iterator itr = servernames.begin();
for(; itr != servernames.end(); ++itr)
if(**itr == servername)
return;
string * ns = new string(servername);
servernames.push_back(ns);
}
const char* InspIRCd::FindServerNamePtr(const std::string &servername)
{
servernamelist::iterator itr = servernames.begin();
for(; itr != servernames.end(); ++itr)
if(**itr == servername)
return (*itr)->c_str();
servernames.push_back(new string(servername));
itr = --servernames.end();
return (*itr)->c_str();
}
bool InspIRCd::FindServerName(const std::string &servername)
{
servernamelist::iterator itr = servernames.begin();
for(; itr != servernames.end(); ++itr)
if(**itr == servername)
return true;
return false;
}
void InspIRCd::Exit(int status)
{
#ifdef WINDOWS
CloseIPC();
#endif
if (SI)
{
SI->SendError("Exiting with status " + ConvToStr(status) + " (" + std::string(ExitCodes[status]) + ")");
SI->Cleanup();
}
exit (status);
}
void InspIRCd::Cleanup()
{
std::vector<std::string> mymodnames;
int MyModCount = this->GetModuleCount();
for (unsigned int i = 0; i < Config->ports.size(); i++)
{
/* This calls the constructor and closes the listening socket */
delete Config->ports[i];
}
Config->ports.clear();
/* Close all client sockets, or the new process inherits them */
for (std::vector<userrec*>::const_iterator i = this->local_users.begin(); i != this->local_users.end(); i++)
{
(*i)->SetWriteError("Server shutdown");
(*i)->CloseSocket();
}
/* We do this more than once, so that any service providers get a
* chance to be unhooked by the modules using them, but then get
* a chance to be removed themsleves.
*/
for (int tries = 0; tries < 3; tries++)
{
MyModCount = this->GetModuleCount();
mymodnames.clear();
/* Unload all modules, so they get a chance to clean up their listeners */
for (int j = 0; j <= MyModCount; j++)
mymodnames.push_back(Config->module_names[j]);
for (int k = 0; k <= MyModCount; k++)
this->UnloadModule(mymodnames[k].c_str());
}
/* Close logging */
this->Logger->Close();
/* Cleanup Server Names */
for(servernamelist::iterator itr = servernames.begin(); itr != servernames.end(); ++itr)
delete (*itr);
#ifdef WINDOWS
/* WSACleanup */
WSACleanup();
#endif
}
void InspIRCd::Restart(const std::string &reason)
{
/* SendError flushes each client's queue,
* regardless of writeability state
*/
this->SendError(reason);
this->Cleanup();
/* Figure out our filename (if theyve renamed it, we're boned) */
std::string me;
#ifdef WINDOWS
char module[MAX_PATH];
if (GetModuleFileName(NULL, module, MAX_PATH))
me = module;
#else
me = Config->MyDir + "/inspircd";
#endif
if (execv(me.c_str(), Config->argv) == -1)
{
/* Will raise a SIGABRT if not trapped */
throw CoreException(std::string("Failed to execv()! error: ") + strerror(errno));
}
}
void InspIRCd::Start()
{
printf_c("\033[1;32mInspire Internet Relay Chat Server, compiled %s at %s\n",__DATE__,__TIME__);
printf_c("(C) InspIRCd Development Team.\033[0m\n\n");
printf_c("Developers:\t\t\033[1;32mBrain, FrostyCoolSlug, w00t, Om, Special, pippijn, peavey, Burlex\033[0m\n");
printf_c("Others:\t\t\t\033[1;32mSee /INFO Output\033[0m\n");
}
void InspIRCd::Rehash(int status)
{
SI->WriteOpers("*** Rehashing config file %s due to SIGHUP",ServerConfig::CleanFilename(SI->ConfigFileName));
SI->CloseLog();
SI->OpenLog(SI->Config->argv, SI->Config->argc);
SI->RehashUsersAndChans();
FOREACH_MOD_I(SI, I_OnGarbageCollect, OnGarbageCollect());
SI->Config->Read(false,NULL);
SI->ResetMaxBans();
SI->Res->Rehash();
FOREACH_MOD_I(SI,I_OnRehash,OnRehash(NULL,""));
SI->BuildISupport();
}
void InspIRCd::ResetMaxBans()
{
for (chan_hash::const_iterator i = chanlist->begin(); i != chanlist->end(); i++)
i->second->ResetMaxBans();
}
/** Because hash_map doesnt free its buckets when we delete items (this is a 'feature')
* we must occasionally rehash the hash (yes really).
* We do this by copying the entries from the old hash to a new hash, causing all
* empty buckets to be weeded out of the hash. We dont do this on a timer, as its
* very expensive, so instead we do it when the user types /REHASH and expects a
* short delay anyway.
*/
void InspIRCd::RehashUsersAndChans()
{
user_hash* old_users = this->clientlist;
chan_hash* old_chans = this->chanlist;
this->clientlist = new user_hash();
this->chanlist = new chan_hash();
for (user_hash::const_iterator n = old_users->begin(); n != old_users->end(); n++)
this->clientlist->insert(*n);
delete old_users;
for (chan_hash::const_iterator n = old_chans->begin(); n != old_chans->end(); n++)
this->chanlist->insert(*n);
delete old_chans;
}
void InspIRCd::CloseLog()
{
this->Logger->Close();
}
void InspIRCd::SetSignals()
{
#ifndef WIN32
signal(SIGALRM, SIG_IGN);
signal(SIGHUP, InspIRCd::Rehash);
signal(SIGPIPE, SIG_IGN);
signal(SIGCHLD, SIG_IGN);
#endif
signal(SIGTERM, InspIRCd::Exit);
}
void InspIRCd::QuickExit(int status)
{
exit(0);
}
bool InspIRCd::DaemonSeed()
{
#ifdef WINDOWS
printf_c("InspIRCd Process ID: \033[1;32m%lu\033[0m\n", GetCurrentProcessId());
return true;
#else
signal(SIGTERM, InspIRCd::QuickExit);
int childpid;
if ((childpid = fork ()) < 0)
return false;
else if (childpid > 0)
{
/* We wait here for the child process to kill us,
* so that the shell prompt doesnt come back over
* the output.
* Sending a kill with a signal of 0 just checks
* if the child pid is still around. If theyre not,
* they threw an error and we should give up.
*/
while (kill(childpid, 0) != -1)
sleep(1);
exit(0);
}
setsid ();
umask (007);
printf("InspIRCd Process ID: \033[1;32m%lu\033[0m\n",(unsigned long)getpid());
signal(SIGTERM, InspIRCd::Exit);
rlimit rl;
if (getrlimit(RLIMIT_CORE, &rl) == -1)
{
this->Log(DEFAULT,"Failed to getrlimit()!");
return false;
}
else
{
rl.rlim_cur = rl.rlim_max;
if (setrlimit(RLIMIT_CORE, &rl) == -1)
this->Log(DEFAULT,"setrlimit() failed, cannot increase coredump size.");
}
return true;
#endif
}
void InspIRCd::WritePID(const std::string &filename)
{
std::string fname = (filename.empty() ? "inspircd.pid" : filename);
if (*(fname.begin()) != '/')
{
std::string::size_type pos;
std::string confpath = this->ConfigFileName;
if ((pos = confpath.rfind("/")) != std::string::npos)
{
/* Leaves us with just the path */
fname = confpath.substr(0, pos) + std::string("/") + fname;
}
}
std::ofstream outfile(fname.c_str());
if (outfile.is_open())
{
outfile << getpid();
outfile.close();
}
else
{
printf("Failed to write PID-file '%s', exiting.\n",fname.c_str());
this->Log(DEFAULT,"Failed to write PID-file '%s', exiting.",fname.c_str());
Exit(EXIT_STATUS_PID);
}
}
std::string InspIRCd::GetRevision()
{
return REVISION;
}
InspIRCd::InspIRCd(int argc, char** argv)
: ModCount(-1), GlobalCulls(this)
{
int found_ports = 0;
FailedPortList pl;
int do_version = 0, do_nofork = 0, do_debug = 0, do_nolog = 0, do_root = 0; /* flag variables */
char c = 0;
modules.resize(255);
factory.resize(255);
memset(&server, 0, sizeof(server));
memset(&client, 0, sizeof(client));
this->unregistered_count = 0;
this->clientlist = new user_hash();
this->chanlist = new chan_hash();
this->Config = new ServerConfig(this);
this->Config->argv = argv;
this->Config->argc = argc;
chdir(Config->GetFullProgDir().c_str());
this->Config->opertypes.clear();
this->Config->operclass.clear();
this->SNO = new SnomaskManager(this);
this->TIME = this->OLDTIME = this->startup_time = time(NULL);
this->time_delta = 0;
this->next_call = this->TIME + 3;
srand(this->TIME);
*this->LogFileName = 0;
strlcpy(this->ConfigFileName, CONFIG_FILE, MAXBUF);
struct option longopts[] =
{
{ "nofork", no_argument, &do_nofork, 1 },
{ "logfile", required_argument, NULL, 'f' },
{ "config", required_argument, NULL, 'c' },
{ "debug", no_argument, &do_debug, 1 },
{ "nolog", no_argument, &do_nolog, 1 },
{ "runasroot", no_argument, &do_root, 1 },
{ "version", no_argument, &do_version, 1 },
{ 0, 0, 0, 0 }
};
while ((c = getopt_long_only(argc, argv, ":f:", longopts, NULL)) != -1)
{
switch (c)
{
case 'f':
/* Log filename was set */
strlcpy(LogFileName, optarg, MAXBUF);
break;
case 'c':
/* Config filename was set */
strlcpy(ConfigFileName, optarg, MAXBUF);
break;
case 0:
/* getopt_long_only() set an int variable, just keep going */
break;
default:
/* Unknown parameter! DANGER, INTRUDER.... err.... yeah. */
printf("Usage: %s [--nofork] [--nolog] [--debug] [--logfile <filename>] [--runasroot] [--version] [--config <config>]\n", argv[0]);
Exit(EXIT_STATUS_ARGV);
break;
}
}
if (do_version)
{
printf("\n%s r%s\n", VERSION, REVISION);
Exit(EXIT_STATUS_NOERROR);
}
#ifdef WIN32
// Handle forking
if(!do_nofork && !owner_processid)
{
DWORD ExitCode = WindowsForkStart(this);
if(ExitCode)
Exit(ExitCode);
}
// Set up winsock
WSADATA wsadata;
WSAStartup(MAKEWORD(2,0), &wsadata);
#endif
if (!ServerConfig::FileExists(this->ConfigFileName))
{
printf("ERROR: Cannot open config file: %s\nExiting...\n", this->ConfigFileName);
this->Log(DEFAULT,"Unable to open config file %s", this->ConfigFileName);
Exit(EXIT_STATUS_CONFIG);
}
this->Start();
/* Set the finished argument values */
Config->nofork = do_nofork;
Config->forcedebug = do_debug;
Config->writelog = !do_nolog;
strlcpy(Config->MyExecutable,argv[0],MAXBUF);
this->OpenLog(argv, argc);
this->stats = new serverstats();
this->Timers = new TimerManager(this);
this->Parser = new CommandParser(this);
this->XLines = new XLineManager(this);
Config->ClearStack();
Config->Read(true, NULL);
if (!do_root)
this->CheckRoot();
else
{
printf("* WARNING * WARNING * WARNING * WARNING * WARNING * \n\n");
printf("YOU ARE RUNNING INSPIRCD AS ROOT. THIS IS UNSUPPORTED\n");
printf("AND IF YOU ARE HACKED, CRACKED, SPINDLED OR MUTILATED\n");
printf("OR ANYTHING ELSE UNEXPECTED HAPPENS TO YOU OR YOUR\n");
printf("SERVER, THEN IT IS YOUR OWN FAULT. IF YOU DID NOT MEAN\n");
printf("TO START INSPIRCD AS ROOT, HIT CTRL+C NOW AND RESTART\n");
printf("THE PROGRAM AS A NORMAL USER. YOU HAVE BEEN WARNED!\n");
printf("\nInspIRCd starting in 20 seconds, ctrl+c to abort...\n");
sleep(20);
}
this->SetSignals();
if (!Config->nofork)
{
if (!this->DaemonSeed())
{
printf("ERROR: could not go into daemon mode. Shutting down.\n");
Log(DEFAULT,"ERROR: could not go into daemon mode. Shutting down.");
Exit(EXIT_STATUS_FORK);
}
}
/* Because of limitations in kqueue on freebsd, we must fork BEFORE we
* initialize the socket engine.
*/
SocketEngineFactory* SEF = new SocketEngineFactory();
SE = SEF->Create(this);
delete SEF;
this->Modes = new ModeParser(this);
this->AddServerName(Config->ServerName);
CheckDie();
int bounditems = BindPorts(true, found_ports, pl);
for(int t = 0; t < 255; t++)
Config->global_implementation[t] = 0;
memset(&Config->implement_lists,0,sizeof(Config->implement_lists));
printf("\n");
this->Res = new DNS(this);
this->LoadAllModules();
/* Just in case no modules were loaded - fix for bug #101 */
this->BuildISupport();
InitializeDisabledCommands(Config->DisabledCommands, this);
if ((Config->ports.size() == 0) && (found_ports > 0))
{
printf("\nERROR: I couldn't bind any ports! Are you sure you didn't start InspIRCd twice?\n");
Log(DEFAULT,"ERROR: I couldn't bind any ports! Are you sure you didn't start InspIRCd twice?");
Exit(EXIT_STATUS_BIND);
}
if (Config->ports.size() != (unsigned int)found_ports)
{
printf("\nWARNING: Not all your client ports could be bound --\nstarting anyway with %d of %d client ports bound.\n\n", bounditems, found_ports);
printf("The following port(s) failed to bind:\n");
int j = 1;
for (FailedPortList::iterator i = pl.begin(); i != pl.end(); i++, j++)
{
printf("%d.\tIP: %s\tPort: %lu\n", j, i->first.empty() ? "<all>" : i->first.c_str(), (unsigned long)i->second);
}
}
#ifndef WINDOWS
if (!Config->nofork)
{
if (kill(getppid(), SIGTERM) == -1)
{
printf("Error killing parent process: %s\n",strerror(errno));
Log(DEFAULT,"Error killing parent process: %s",strerror(errno));
}
}
if (isatty(0) && isatty(1) && isatty(2))
{
/* We didn't start from a TTY, we must have started from a background process -
* e.g. we are restarting, or being launched by cron. Dont kill parent, and dont
* close stdin/stdout
*/
if (!do_nofork)
{
fclose(stdin);
fclose(stderr);
fclose(stdout);
}
else
{
Log(DEFAULT,"Keeping pseudo-tty open as we are running in the foreground.");
}
}
#else
InitIPC();
if(!Config->nofork)
{
WindowsForkKillOwner(this);
FreeConsole();
}
#endif
printf("\nInspIRCd is now running!\n");
Log(DEFAULT,"Startup complete.");
this->WritePID(Config->PID);
}
std::string InspIRCd::GetVersionString()
{
char versiondata[MAXBUF];
char dnsengine[] = "singlethread-object";
if (*Config->CustomVersion)
{
snprintf(versiondata,MAXBUF,"%s %s :%s",VERSION,Config->ServerName,Config->CustomVersion);
}
else
{
snprintf(versiondata,MAXBUF,"%s %s :%s [FLAGS=%s,%s,%s]",VERSION,Config->ServerName,SYSTEM,REVISION,SE->GetName().c_str(),dnsengine);
}
return versiondata;
}
char* InspIRCd::ModuleError()
{
return MODERR;
}
void InspIRCd::EraseFactory(int j)
{
int v = 0;
for (std::vector<ircd_module*>::iterator t = factory.begin(); t != factory.end(); t++)
{
if (v == j)
{
delete *t;
factory.erase(t);
factory.push_back(NULL);
return;
}
v++;
}
}
void InspIRCd::EraseModule(int j)
{
int v1 = 0;
for (ModuleList::iterator m = modules.begin(); m!= modules.end(); m++)
{
if (v1 == j)
{
DELETE(*m);
modules.erase(m);
modules.push_back(NULL);
break;
}
v1++;
}
int v2 = 0;
for (std::vector<std::string>::iterator v = Config->module_names.begin(); v != Config->module_names.end(); v++)
{
if (v2 == j)
{
Config->module_names.erase(v);
break;
}
v2++;
}
}
void InspIRCd::MoveTo(std::string modulename,int slot)
{
unsigned int v2 = 256;
for (unsigned int v = 0; v < Config->module_names.size(); v++)
{
if (Config->module_names[v] == modulename)
{
// found an instance, swap it with the item at the end
v2 = v;
break;
}
}
if ((v2 != (unsigned int)slot) && (v2 < 256))
{
// Swap the module names over
Config->module_names[v2] = Config->module_names[slot];
Config->module_names[slot] = modulename;
// now swap the module factories
ircd_module* temp = factory[v2];
factory[v2] = factory[slot];
factory[slot] = temp;
// now swap the module objects
Module* temp_module = modules[v2];
modules[v2] = modules[slot];
modules[slot] = temp_module;
// now swap the implement lists (we dont
// need to swap the global or recount it)
for (int n = 0; n < 255; n++)
{
char x = Config->implement_lists[v2][n];
Config->implement_lists[v2][n] = Config->implement_lists[slot][n];
Config->implement_lists[slot][n] = x;
}
}
}
void InspIRCd::MoveAfter(std::string modulename, std::string after)
{
for (unsigned int v = 0; v < Config->module_names.size(); v++)
{
if (Config->module_names[v] == after)
{
MoveTo(modulename, v);
return;
}
}
}
void InspIRCd::MoveBefore(std::string modulename, std::string before)
{
for (unsigned int v = 0; v < Config->module_names.size(); v++)
{
if (Config->module_names[v] == before)
{
if (v > 0)
{
MoveTo(modulename, v-1);
}
else
{
MoveTo(modulename, v);
}
return;
}
}
}
void InspIRCd::MoveToFirst(std::string modulename)
{
MoveTo(modulename,0);
}
void InspIRCd::MoveToLast(std::string modulename)
{
MoveTo(modulename,this->GetModuleCount());
}
void InspIRCd::BuildISupport()
{
// the neatest way to construct the initial 005 numeric, considering the number of configure constants to go in it...
std::stringstream v;
v << "WALLCHOPS WALLVOICES MODES=" << MAXMODES-1 << " CHANTYPES=# PREFIX=" << this->Modes->BuildPrefixes() << " MAP MAXCHANNELS=" << Config->MaxChans << " MAXBANS=60 VBANLIST NICKLEN=" << NICKMAX-1;
v << " CASEMAPPING=rfc1459 STATUSMSG=@%+ CHARSET=ascii TOPICLEN=" << MAXTOPIC << " KICKLEN=" << MAXKICK << " MAXTARGETS=" << Config->MaxTargets << " AWAYLEN=";
v << MAXAWAY << " CHANMODES=" << this->Modes->ChanModes() << " FNC NETWORK=" << Config->Network << " MAXPARA=32 ELIST=MU";
Config->data005 = v.str();
FOREACH_MOD_I(this,I_On005Numeric,On005Numeric(Config->data005));
Config->Update005();
}
bool InspIRCd::UnloadModule(const char* filename)
{
std::string filename_str = filename;
for (unsigned int j = 0; j != Config->module_names.size(); j++)
{
if (Config->module_names[j] == filename_str)
{
if (modules[j]->GetVersion().Flags & VF_STATIC)
{
this->Log(DEFAULT,"Failed to unload STATIC module %s",filename);
snprintf(MODERR,MAXBUF,"Module not unloadable (marked static)");
return false;
}
std::pair<int,std::string> intercount = GetInterfaceInstanceCount(modules[j]);
if (intercount.first > 0)
{
this->Log(DEFAULT,"Failed to unload module %s, being used by %d other(s) via interface '%s'",filename, intercount.first, intercount.second.c_str());
snprintf(MODERR,MAXBUF,"Module not unloadable (Still in use by %d other module%s which %s using its interface '%s') -- unload dependent modules first!",
intercount.first,
intercount.first > 1 ? "s" : "",
intercount.first > 1 ? "are" : "is",
intercount.second.c_str());
return false;
}
/* Give the module a chance to tidy out all its metadata */
for (chan_hash::iterator c = this->chanlist->begin(); c != this->chanlist->end(); c++)
{
modules[j]->OnCleanup(TYPE_CHANNEL,c->second);
}
for (user_hash::iterator u = this->clientlist->begin(); u != this->clientlist->end(); u++)
{
modules[j]->OnCleanup(TYPE_USER,u->second);
}
/* Tidy up any dangling resolvers */
this->Res->CleanResolvers(modules[j]);
FOREACH_MOD_I(this,I_OnUnloadModule,OnUnloadModule(modules[j],Config->module_names[j]));
for(int t = 0; t < 255; t++)
{
Config->global_implementation[t] -= Config->implement_lists[j][t];
}
/* We have to renumber implement_lists after unload because the module numbers change!
*/
for(int j2 = j; j2 < 254; j2++)
{
for(int t = 0; t < 255; t++)
{
Config->implement_lists[j2][t] = Config->implement_lists[j2+1][t];
}
}
// found the module
Parser->RemoveCommands(filename);
this->EraseModule(j);
this->EraseFactory(j);
this->Log(DEFAULT,"Module %s unloaded",filename);
this->ModCount--;
BuildISupport();
return true;
}
}
this->Log(DEFAULT,"Module %s is not loaded, cannot unload it!",filename);
snprintf(MODERR,MAXBUF,"Module not loaded");
return false;
}
bool InspIRCd::LoadModule(const char* filename)
{
/* Do we have a glob pattern in the filename?
* The user wants to load multiple modules which
* match the pattern.
*/
if (strchr(filename,'*') || (strchr(filename,'?')))
{
int n_match = 0;
DIR* library = opendir(Config->ModPath);
if (library)
{
/* Try and locate and load all modules matching the pattern */
dirent* entry = NULL;
while ((entry = readdir(library)))
{
if (this->MatchText(entry->d_name, filename))
{
if (!this->LoadModule(entry->d_name))
n_match++;
}
}
closedir(library);
}
/* Loadmodule will now return false if any one of the modules failed
* to load (but wont abort when it encounters a bad one) and when 1 or
* more modules were actually loaded.
*/
return (n_match > 0);
}
char modfile[MAXBUF];
snprintf(modfile,MAXBUF,"%s/%s",Config->ModPath,filename);
std::string filename_str = filename;
if (!ServerConfig::DirValid(modfile))
{
this->Log(DEFAULT,"Module %s is not within the modules directory.",modfile);
snprintf(MODERR,MAXBUF,"Module %s is not within the modules directory.",modfile);
return false;
}
if (ServerConfig::FileExists(modfile))
{
for (unsigned int j = 0; j < Config->module_names.size(); j++)
{
if (Config->module_names[j] == filename_str)
{
this->Log(DEFAULT,"Module %s is already loaded, cannot load a module twice!",modfile);
snprintf(MODERR,MAXBUF,"Module already loaded");
return false;
}
}
Module* m = NULL;
ircd_module* a = NULL;
try
{
a = new ircd_module(this, modfile);
factory[this->ModCount+1] = a;
if (factory[this->ModCount+1]->LastError())
{
this->Log(DEFAULT,"Unable to load %s: %s",modfile,factory[this->ModCount+1]->LastError());
snprintf(MODERR,MAXBUF,"Loader/Linker error: %s",factory[this->ModCount+1]->LastError());
return false;
}
if ((long)factory[this->ModCount+1]->factory != -1)
{
m = factory[this->ModCount+1]->factory->CreateModule(this);
Version v = m->GetVersion();
if (v.API != API_VERSION)
{
delete m;
this->Log(DEFAULT,"Unable to load %s: Incorrect module API version: %d (our version: %d)",modfile,v.API,API_VERSION);
snprintf(MODERR,MAXBUF,"Loader/Linker error: Incorrect module API version: %d (our version: %d)",v.API,API_VERSION);
return false;
}
else
{
this->Log(DEFAULT,"New module introduced: %s (API version %d, Module version %d.%d.%d.%d)%s", filename, v.API, v.Major, v.Minor, v.Revision, v.Build, (!(v.Flags & VF_VENDOR) ? " [3rd Party]" : " [Vendor]"));
}
modules[this->ModCount+1] = m;
/* save the module and the module's classfactory, if
* this isnt done, random crashes can occur :/ */
Config->module_names.push_back(filename);
char* x = &Config->implement_lists[this->ModCount+1][0];
for(int t = 0; t < 255; t++)
x[t] = 0;
modules[this->ModCount+1]->Implements(x);
for(int t = 0; t < 255; t++)
Config->global_implementation[t] += Config->implement_lists[this->ModCount+1][t];
}
else
{
this->Log(DEFAULT,"Unable to load %s",modfile);
snprintf(MODERR,MAXBUF,"Factory function failed: Probably missing init_module() entrypoint.");
return false;
}
}
catch (CoreException& modexcept)
{
this->Log(DEFAULT,"Unable to load %s: %s",modfile,modexcept.GetReason());
snprintf(MODERR,MAXBUF,"Factory function of %s threw an exception: %s", modexcept.GetSource(), modexcept.GetReason());
return false;
}
}
else
{
this->Log(DEFAULT,"InspIRCd: startup: Module Not Found %s",modfile);
snprintf(MODERR,MAXBUF,"Module file could not be found");
return false;
}
this->ModCount++;
FOREACH_MOD_I(this,I_OnLoadModule,OnLoadModule(modules[this->ModCount],filename_str));
// now work out which modules, if any, want to move to the back of the queue,
// and if they do, move them there.
std::vector<std::string> put_to_back;
std::vector<std::string> put_to_front;
std::map<std::string,std::string> put_before;
std::map<std::string,std::string> put_after;
for (unsigned int j = 0; j < Config->module_names.size(); j++)
{
if (modules[j]->Prioritize() == PRIORITY_LAST)
put_to_back.push_back(Config->module_names[j]);
else if (modules[j]->Prioritize() == PRIORITY_FIRST)
put_to_front.push_back(Config->module_names[j]);
else if ((modules[j]->Prioritize() & 0xFF) == PRIORITY_BEFORE)
put_before[Config->module_names[j]] = Config->module_names[modules[j]->Prioritize() >> 8];
else if ((modules[j]->Prioritize() & 0xFF) == PRIORITY_AFTER)
put_after[Config->module_names[j]] = Config->module_names[modules[j]->Prioritize() >> 8];
}
for (unsigned int j = 0; j < put_to_back.size(); j++)
MoveToLast(put_to_back[j]);
for (unsigned int j = 0; j < put_to_front.size(); j++)
MoveToFirst(put_to_front[j]);
for (std::map<std::string,std::string>::iterator j = put_before.begin(); j != put_before.end(); j++)
MoveBefore(j->first,j->second);
for (std::map<std::string,std::string>::iterator j = put_after.begin(); j != put_after.end(); j++)
MoveAfter(j->first,j->second);
BuildISupport();
return true;
}
void InspIRCd::DoOneIteration(bool process_module_sockets)
{
#ifndef WIN32
static rusage ru;
#else
static time_t uptime;
static struct tm * stime;
static char window_title[100];
#endif
/* time() seems to be a pretty expensive syscall, so avoid calling it too much.
* Once per loop iteration is pleanty.
*/
OLDTIME = TIME;
TIME = time(NULL);
/* Run background module timers every few seconds
* (the docs say modules shouldnt rely on accurate
* timing using this event, so we dont have to
* time this exactly).
*/
if (TIME != OLDTIME)
{
if (TIME < OLDTIME)
WriteOpers("*** \002EH?!\002 -- Time is flowing BACKWARDS in this dimension! Clock drifted backwards %d secs.",abs(OLDTIME-TIME));
if ((TIME % 3600) == 0)
{
this->RehashUsersAndChans();
FOREACH_MOD_I(this, I_OnGarbageCollect, OnGarbageCollect());
}
Timers->TickTimers(TIME);
this->DoBackgroundUserStuff(TIME);
if ((TIME % 5) == 0)
{
XLines->expire_lines();
FOREACH_MOD_I(this,I_OnBackgroundTimer,OnBackgroundTimer(TIME));
Timers->TickMissedTimers(TIME);
}
#ifndef WIN32
/* Same change as in cmd_stats.cpp, use RUSAGE_SELF rather than '0' -- Om */
if (!getrusage(RUSAGE_SELF, &ru))
{
gettimeofday(&this->stats->LastSampled, NULL);
this->stats->LastCPU = ru.ru_utime;
}
#else
CheckIPC(this);
if(Config->nofork)
{
uptime = Time() - startup_time;
stime = gmtime(&uptime);
snprintf(window_title, 100, "InspIRCd - %u clients, %u accepted connections - Up %u days, %.2u:%.2u:%.2u",
LocalUserCount(), stats->statsAccept, stime->tm_yday, stime->tm_hour, stime->tm_min, stime->tm_sec);
SetConsoleTitle(window_title);
}
#endif
}
/* Call the socket engine to wait on the active
* file descriptors. The socket engine has everything's
* descriptors in its list... dns, modules, users,
* servers... so its nice and easy, just one call.
* This will cause any read or write events to be
* dispatched to their handlers.
*/
SE->DispatchEvents();
/* if any users was quit, take them out */
GlobalCulls.Apply();
/* If any inspsockets closed, remove them */
for (std::map<InspSocket*,InspSocket*>::iterator x = SocketCull.begin(); x != SocketCull.end(); ++x)
{
SE->DelFd(x->second);
x->second->Close();
delete x->second;
}
SocketCull.clear();
}
int InspIRCd::Run()
{
while (true)
{
DoOneIteration(true);
}
/* This is never reached -- we hope! */
return 0;
}
/**********************************************************************************/
/**
* An ircd in four lines! bwahahaha. ahahahahaha. ahahah *cough*.
*/
int main(int argc, char** argv)
{
SI = new InspIRCd(argc, argv);
SI->Run();
delete SI;
return 0;
}
/* this returns true when all modules are satisfied that the user should be allowed onto the irc server
* (until this returns true, a user will block in the waiting state, waiting to connect up to the
* registration timeout maximum seconds)
*/
bool InspIRCd::AllModulesReportReady(userrec* user)
{
if (!Config->global_implementation[I_OnCheckReady])
return true;
for (int i = 0; i <= this->GetModuleCount(); i++)
{
if (Config->implement_lists[i][I_OnCheckReady])
{
int res = modules[i]->OnCheckReady(user);
if (!res)
return false;
}
}
return true;
}
int InspIRCd::GetModuleCount()
{
return this->ModCount;
}
time_t InspIRCd::Time(bool delta)
{
if (delta)
return TIME + time_delta;
return TIME;
}
int InspIRCd::SetTimeDelta(int delta)
{
int old = time_delta;
time_delta = delta;
this->Log(DEBUG, "Time delta set to %d (was %d)", time_delta, old);
return old;
}
void InspIRCd::AddLocalClone(userrec* user)
{
clonemap::iterator x = local_clones.find(user->GetIPString());
if (x != local_clones.end())
x->second++;
else
local_clones[user->GetIPString()] = 1;
}
void InspIRCd::AddGlobalClone(userrec* user)
{
clonemap::iterator y = global_clones.find(user->GetIPString());
if (y != global_clones.end())
y->second++;
else
global_clones[user->GetIPString()] = 1;
}
int InspIRCd::GetTimeDelta()
{
return time_delta;
}
bool FileLogger::Readable()
{
return false;
}
void FileLogger::HandleEvent(EventType et, int errornum)
{
this->WriteLogLine("");
if (log)
ServerInstance->SE->DelFd(this);
}
void FileLogger::WriteLogLine(const std::string &line)
{
if (line.length())
buffer.append(line);
if (log)
{
int written = fprintf(log,"%s",buffer.c_str());
#ifdef WINDOWS
buffer.clear();
#else
if ((written >= 0) && (written < (int)buffer.length()))
{
buffer.erase(0, buffer.length());
ServerInstance->SE->AddFd(this);
}
else if (written == -1)
{
if (errno == EAGAIN)
ServerInstance->SE->AddFd(this);
}
else
{
/* Wrote the whole buffer, and no need for write callback */
buffer.clear();
}
#endif
if (writeops++ % 20)
{
fflush(log);
}
}
}
void FileLogger::Close()
{
if (log)
{
/* Burlex: Windows assumes nonblocking on FILE* pointers anyway, and also "file" fd's aren't the same
* as socket fd's. */
#ifndef WIN32
int flags = fcntl(fileno(log), F_GETFL, 0);
fcntl(fileno(log), F_SETFL, flags ^ O_NONBLOCK);
#endif
if (buffer.size())
fprintf(log,"%s",buffer.c_str());
#ifndef WINDOWS
ServerInstance->SE->DelFd(this);
#endif
fflush(log);
fclose(log);
}
buffer.clear();
}
FileLogger::FileLogger(InspIRCd* Instance, FILE* logfile) : ServerInstance(Instance), log(logfile), writeops(0)
{
if (log)
{
irc::sockets::NonBlocking(fileno(log));
this->SetFd(fileno(log));
buffer.clear();
}
}
FileLogger::~FileLogger()
{
this->Close();
}
\ No newline at end of file +/* +------------------------------------+ + * | 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 <signal.h> + +#ifndef WIN32 +#include <dirent.h> +#include <unistd.h> +#include <sys/resource.h> +#include <dlfcn.h> +#include <getopt.h> + +/* Some systems don't define RUSAGE_SELF. This should fix them. */ +#ifndef RUSAGE_SELF + #define RUSAGE_SELF 0 +#endif + +#endif + +#include <exception> +#include <fstream> +#include "modules.h" +#include "mode.h" +#include "xline.h" +#include "socketengine.h" +#include "inspircd_se_config.h" +#include "socket.h" +#include "typedefs.h" +#include "command_parse.h" +#include "exitcodes.h" + +#ifdef WIN32 + +/* This MUST remain static and delcared outside the class, so that WriteProcessMemory can reference it properly */ +static DWORD owner_processid = 0; + +DWORD WindowsForkStart(InspIRCd * Instance) +{ + /* Windows implementation of fork() :P */ + + char module[MAX_PATH]; + if(!GetModuleFileName(NULL, module, MAX_PATH)) + { + printf("GetModuleFileName() failed.\n"); + return false; + } + + STARTUPINFO startupinfo; + PROCESS_INFORMATION procinfo; + ZeroMemory(&startupinfo, sizeof(STARTUPINFO)); + ZeroMemory(&procinfo, sizeof(PROCESS_INFORMATION)); + + // Fill in the startup info struct + GetStartupInfo(&startupinfo); + + /* Default creation flags create the processes suspended */ + DWORD startupflags = CREATE_SUSPENDED; + + /* On windows 2003/XP and above, we can use the value + * CREATE_PRESERVE_CODE_AUTHZ_LEVEL which gives more access + * to the process which we may require on these operating systems. + */ + OSVERSIONINFO vi; + vi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); + GetVersionEx(&vi); + if ((vi.dwMajorVersion >= 5) && (vi.dwMinorVersion > 0)) + startupflags |= CREATE_PRESERVE_CODE_AUTHZ_LEVEL; + + // Launch our "forked" process. + BOOL bSuccess = CreateProcess ( module, // Module (exe) filename + strdup(GetCommandLine()), // Command line (exe plus parameters from the OS) + // NOTE: We cannot return the direct value of the + // GetCommandLine function here, as the pointer is + // passed straight to the child process, and will be + // invalid once we exit as it goes out of context. + // strdup() seems ok, though. + 0, // PROCESS_SECURITY_ATTRIBUTES + 0, // THREAD_SECURITY_ATTRIBUTES + TRUE, // We went to inherit handles. + startupflags, // Allow us full access to the process and suspend it. + 0, // ENVIRONMENT + 0, // CURRENT_DIRECTORY + &startupinfo, // startup info + &procinfo); // process info + + if(!bSuccess) + { + printf("CreateProcess() error: %s\n", dlerror()); + return false; + } + + // Set the owner process id in the target process. + SIZE_T written = 0; + DWORD pid = GetCurrentProcessId(); + if(!WriteProcessMemory(procinfo.hProcess, &owner_processid, &pid, sizeof(DWORD), &written) || written != sizeof(DWORD)) + { + printf("WriteProcessMemory() failed: %s\n", dlerror()); + return false; + } + + // Resume the other thread (let it start) + ResumeThread(procinfo.hThread); + + // Wait for the new process to kill us. If there is some error, the new process will end and we will end up at the next line. + WaitForSingleObject(procinfo.hProcess, INFINITE); + + // If we hit this it means startup failed, default to 14 if this fails. + DWORD ExitCode = 14; + GetExitCodeProcess(procinfo.hProcess, &ExitCode); + CloseHandle(procinfo.hThread); + CloseHandle(procinfo.hProcess); + return ExitCode; +} + +void WindowsForkKillOwner(InspIRCd * Instance) +{ + HANDLE hProcess = OpenProcess(PROCESS_TERMINATE, FALSE, owner_processid); + if(!hProcess || !owner_processid) + { + printf("Could not open process id %u: %s.\n", owner_processid, dlerror()); + Instance->Exit(14); + } + + // die die die + if(!TerminateProcess(hProcess, 0)) + { + printf("Could not TerminateProcess(): %s\n", dlerror()); + Instance->Exit(14); + } + + CloseHandle(hProcess); +} + +#endif + +using irc::sockets::NonBlocking; +using irc::sockets::Blocking; +using irc::sockets::insp_ntoa; +using irc::sockets::insp_inaddr; +using irc::sockets::insp_sockaddr; + +InspIRCd* SI = NULL; + +/* Burlex: Moved from exitcodes.h -- due to duplicate symbols */ +const char* ExitCodes[] = +{ + "No error", /* 0 */ + "DIE command", /* 1 */ + "execv() failed", /* 2 */ + "Internal error", /* 3 */ + "Config file error", /* 4 */ + "Logfile error", /* 5 */ + "POSIX fork failed", /* 6 */ + "Bad commandline parameters", /* 7 */ + "No ports could be bound", /* 8 */ + "Can't write PID file", /* 9 */ + "SocketEngine could not initialize", /* 10 */ + "Refusing to start up as root", /* 11 */ + "Found a <die> tag!", /* 12 */ + "Couldn't load module on startup", /* 13 */ + "Could not create windows forked process", /* 14 */ + "Received SIGTERM", /* 15 */ +}; + +void InspIRCd::AddServerName(const std::string &servername) +{ + servernamelist::iterator itr = servernames.begin(); + for(; itr != servernames.end(); ++itr) + if(**itr == servername) + return; + + string * ns = new string(servername); + servernames.push_back(ns); +} + +const char* InspIRCd::FindServerNamePtr(const std::string &servername) +{ + servernamelist::iterator itr = servernames.begin(); + for(; itr != servernames.end(); ++itr) + if(**itr == servername) + return (*itr)->c_str(); + + servernames.push_back(new string(servername)); + itr = --servernames.end(); + return (*itr)->c_str(); +} + +bool InspIRCd::FindServerName(const std::string &servername) +{ + servernamelist::iterator itr = servernames.begin(); + for(; itr != servernames.end(); ++itr) + if(**itr == servername) + return true; + return false; +} + +void InspIRCd::Exit(int status) +{ +#ifdef WINDOWS + CloseIPC(); +#endif + if (SI) + { + SI->SendError("Exiting with status " + ConvToStr(status) + " (" + std::string(ExitCodes[status]) + ")"); + SI->Cleanup(); + } + exit (status); +} + +void InspIRCd::Cleanup() +{ + std::vector<std::string> mymodnames; + int MyModCount = this->GetModuleCount(); + + for (unsigned int i = 0; i < Config->ports.size(); i++) + { + /* This calls the constructor and closes the listening socket */ + delete Config->ports[i]; + } + + Config->ports.clear(); + + /* Close all client sockets, or the new process inherits them */ + for (std::vector<userrec*>::const_iterator i = this->local_users.begin(); i != this->local_users.end(); i++) + { + (*i)->SetWriteError("Server shutdown"); + (*i)->CloseSocket(); + } + + /* We do this more than once, so that any service providers get a + * chance to be unhooked by the modules using them, but then get + * a chance to be removed themsleves. + */ + for (int tries = 0; tries < 3; tries++) + { + MyModCount = this->GetModuleCount(); + mymodnames.clear(); + + /* Unload all modules, so they get a chance to clean up their listeners */ + for (int j = 0; j <= MyModCount; j++) + mymodnames.push_back(Config->module_names[j]); + + for (int k = 0; k <= MyModCount; k++) + this->UnloadModule(mymodnames[k].c_str()); + } + + /* Close logging */ + this->Logger->Close(); + + /* Cleanup Server Names */ + for(servernamelist::iterator itr = servernames.begin(); itr != servernames.end(); ++itr) + delete (*itr); + +#ifdef WINDOWS + /* WSACleanup */ + WSACleanup(); +#endif +} + +void InspIRCd::Restart(const std::string &reason) +{ + /* SendError flushes each client's queue, + * regardless of writeability state + */ + this->SendError(reason); + + this->Cleanup(); + + /* Figure out our filename (if theyve renamed it, we're boned) */ + std::string me; + +#ifdef WINDOWS + char module[MAX_PATH]; + if (GetModuleFileName(NULL, module, MAX_PATH)) + me = module; +#else + me = Config->MyDir + "/inspircd"; +#endif + + if (execv(me.c_str(), Config->argv) == -1) + { + /* Will raise a SIGABRT if not trapped */ + throw CoreException(std::string("Failed to execv()! error: ") + strerror(errno)); + } +} + +void InspIRCd::Start() +{ + printf_c("\033[1;32mInspire Internet Relay Chat Server, compiled %s at %s\n",__DATE__,__TIME__); + printf_c("(C) InspIRCd Development Team.\033[0m\n\n"); + printf_c("Developers:\t\t\033[1;32mBrain, FrostyCoolSlug, w00t, Om, Special, pippijn, peavey, Burlex\033[0m\n"); + printf_c("Others:\t\t\t\033[1;32mSee /INFO Output\033[0m\n"); +} + +void InspIRCd::Rehash(int status) +{ + SI->WriteOpers("*** Rehashing config file %s due to SIGHUP",ServerConfig::CleanFilename(SI->ConfigFileName)); + SI->CloseLog(); + SI->OpenLog(SI->Config->argv, SI->Config->argc); + SI->RehashUsersAndChans(); + FOREACH_MOD_I(SI, I_OnGarbageCollect, OnGarbageCollect()); + SI->Config->Read(false,NULL); + SI->ResetMaxBans(); + SI->Res->Rehash(); + FOREACH_MOD_I(SI,I_OnRehash,OnRehash(NULL,"")); + SI->BuildISupport(); +} + +void InspIRCd::ResetMaxBans() +{ + for (chan_hash::const_iterator i = chanlist->begin(); i != chanlist->end(); i++) + i->second->ResetMaxBans(); +} + + +/** Because hash_map doesnt free its buckets when we delete items (this is a 'feature') + * we must occasionally rehash the hash (yes really). + * We do this by copying the entries from the old hash to a new hash, causing all + * empty buckets to be weeded out of the hash. We dont do this on a timer, as its + * very expensive, so instead we do it when the user types /REHASH and expects a + * short delay anyway. + */ +void InspIRCd::RehashUsersAndChans() +{ + user_hash* old_users = this->clientlist; + chan_hash* old_chans = this->chanlist; + + this->clientlist = new user_hash(); + this->chanlist = new chan_hash(); + + for (user_hash::const_iterator n = old_users->begin(); n != old_users->end(); n++) + this->clientlist->insert(*n); + + delete old_users; + + for (chan_hash::const_iterator n = old_chans->begin(); n != old_chans->end(); n++) + this->chanlist->insert(*n); + + delete old_chans; +} + +void InspIRCd::CloseLog() +{ + this->Logger->Close(); +} + +void InspIRCd::SetSignals() +{ +#ifndef WIN32 + signal(SIGALRM, SIG_IGN); + signal(SIGHUP, InspIRCd::Rehash); + signal(SIGPIPE, SIG_IGN); + signal(SIGCHLD, SIG_IGN); +#endif + signal(SIGTERM, InspIRCd::Exit); +} + +void InspIRCd::QuickExit(int status) +{ + exit(0); +} + +bool InspIRCd::DaemonSeed() +{ +#ifdef WINDOWS + printf_c("InspIRCd Process ID: \033[1;32m%lu\033[0m\n", GetCurrentProcessId()); + return true; +#else + signal(SIGTERM, InspIRCd::QuickExit); + + int childpid; + if ((childpid = fork ()) < 0) + return false; + else if (childpid > 0) + { + /* We wait here for the child process to kill us, + * so that the shell prompt doesnt come back over + * the output. + * Sending a kill with a signal of 0 just checks + * if the child pid is still around. If theyre not, + * they threw an error and we should give up. + */ + while (kill(childpid, 0) != -1) + sleep(1); + exit(0); + } + setsid (); + umask (007); + printf("InspIRCd Process ID: \033[1;32m%lu\033[0m\n",(unsigned long)getpid()); + + signal(SIGTERM, InspIRCd::Exit); + + rlimit rl; + if (getrlimit(RLIMIT_CORE, &rl) == -1) + { + this->Log(DEFAULT,"Failed to getrlimit()!"); + return false; + } + else + { + rl.rlim_cur = rl.rlim_max; + if (setrlimit(RLIMIT_CORE, &rl) == -1) + this->Log(DEFAULT,"setrlimit() failed, cannot increase coredump size."); + } + + return true; +#endif +} + +void InspIRCd::WritePID(const std::string &filename) +{ + std::string fname = (filename.empty() ? "inspircd.pid" : filename); + if (*(fname.begin()) != '/') + { + std::string::size_type pos; + std::string confpath = this->ConfigFileName; + if ((pos = confpath.rfind("/")) != std::string::npos) + { + /* Leaves us with just the path */ + fname = confpath.substr(0, pos) + std::string("/") + fname; + } + } + std::ofstream outfile(fname.c_str()); + if (outfile.is_open()) + { + outfile << getpid(); + outfile.close(); + } + else + { + printf("Failed to write PID-file '%s', exiting.\n",fname.c_str()); + this->Log(DEFAULT,"Failed to write PID-file '%s', exiting.",fname.c_str()); + Exit(EXIT_STATUS_PID); + } +} + +std::string InspIRCd::GetRevision() +{ + return REVISION; +} + +InspIRCd::InspIRCd(int argc, char** argv) + : ModCount(-1), GlobalCulls(this) +{ + int found_ports = 0; + FailedPortList pl; + int do_version = 0, do_nofork = 0, do_debug = 0, do_nolog = 0, do_root = 0; /* flag variables */ + char c = 0; + + modules.resize(255); + factory.resize(255); + memset(&server, 0, sizeof(server)); + memset(&client, 0, sizeof(client)); + + this->unregistered_count = 0; + + this->clientlist = new user_hash(); + this->chanlist = new chan_hash(); + + this->Config = new ServerConfig(this); + + this->Config->argv = argv; + this->Config->argc = argc; + + chdir(Config->GetFullProgDir().c_str()); + + this->Config->opertypes.clear(); + this->Config->operclass.clear(); + this->SNO = new SnomaskManager(this); + this->TIME = this->OLDTIME = this->startup_time = time(NULL); + this->time_delta = 0; + this->next_call = this->TIME + 3; + srand(this->TIME); + + *this->LogFileName = 0; + strlcpy(this->ConfigFileName, CONFIG_FILE, MAXBUF); + + struct option longopts[] = + { + { "nofork", no_argument, &do_nofork, 1 }, + { "logfile", required_argument, NULL, 'f' }, + { "config", required_argument, NULL, 'c' }, + { "debug", no_argument, &do_debug, 1 }, + { "nolog", no_argument, &do_nolog, 1 }, + { "runasroot", no_argument, &do_root, 1 }, + { "version", no_argument, &do_version, 1 }, + { 0, 0, 0, 0 } + }; + + while ((c = getopt_long_only(argc, argv, ":f:", longopts, NULL)) != -1) + { + switch (c) + { + case 'f': + /* Log filename was set */ + strlcpy(LogFileName, optarg, MAXBUF); + break; + case 'c': + /* Config filename was set */ + strlcpy(ConfigFileName, optarg, MAXBUF); + break; + case 0: + /* getopt_long_only() set an int variable, just keep going */ + break; + default: + /* Unknown parameter! DANGER, INTRUDER.... err.... yeah. */ + printf("Usage: %s [--nofork] [--nolog] [--debug] [--logfile <filename>] [--runasroot] [--version] [--config <config>]\n", argv[0]); + Exit(EXIT_STATUS_ARGV); + break; + } + } + + if (do_version) + { + printf("\n%s r%s\n", VERSION, REVISION); + Exit(EXIT_STATUS_NOERROR); + } + +#ifdef WIN32 + + // Handle forking + if(!do_nofork && !owner_processid) + { + DWORD ExitCode = WindowsForkStart(this); + if(ExitCode) + Exit(ExitCode); + } + + // Set up winsock + WSADATA wsadata; + WSAStartup(MAKEWORD(2,0), &wsadata); + +#endif + if (!ServerConfig::FileExists(this->ConfigFileName)) + { + printf("ERROR: Cannot open config file: %s\nExiting...\n", this->ConfigFileName); + this->Log(DEFAULT,"Unable to open config file %s", this->ConfigFileName); + Exit(EXIT_STATUS_CONFIG); + } + + this->Start(); + + /* Set the finished argument values */ + Config->nofork = do_nofork; + Config->forcedebug = do_debug; + Config->writelog = !do_nolog; + + strlcpy(Config->MyExecutable,argv[0],MAXBUF); + + this->OpenLog(argv, argc); + + this->stats = new serverstats(); + this->Timers = new TimerManager(this); + this->Parser = new CommandParser(this); + this->XLines = new XLineManager(this); + Config->ClearStack(); + Config->Read(true, NULL); + + if (!do_root) + this->CheckRoot(); + else + { + printf("* WARNING * WARNING * WARNING * WARNING * WARNING * \n\n"); + printf("YOU ARE RUNNING INSPIRCD AS ROOT. THIS IS UNSUPPORTED\n"); + printf("AND IF YOU ARE HACKED, CRACKED, SPINDLED OR MUTILATED\n"); + printf("OR ANYTHING ELSE UNEXPECTED HAPPENS TO YOU OR YOUR\n"); + printf("SERVER, THEN IT IS YOUR OWN FAULT. IF YOU DID NOT MEAN\n"); + printf("TO START INSPIRCD AS ROOT, HIT CTRL+C NOW AND RESTART\n"); + printf("THE PROGRAM AS A NORMAL USER. YOU HAVE BEEN WARNED!\n"); + printf("\nInspIRCd starting in 20 seconds, ctrl+c to abort...\n"); + sleep(20); + } + + this->SetSignals(); + + if (!Config->nofork) + { + if (!this->DaemonSeed()) + { + printf("ERROR: could not go into daemon mode. Shutting down.\n"); + Log(DEFAULT,"ERROR: could not go into daemon mode. Shutting down."); + Exit(EXIT_STATUS_FORK); + } + } + + + /* Because of limitations in kqueue on freebsd, we must fork BEFORE we + * initialize the socket engine. + */ + SocketEngineFactory* SEF = new SocketEngineFactory(); + SE = SEF->Create(this); + delete SEF; + + this->Modes = new ModeParser(this); + this->AddServerName(Config->ServerName); + CheckDie(); + int bounditems = BindPorts(true, found_ports, pl); + + for(int t = 0; t < 255; t++) + Config->global_implementation[t] = 0; + + memset(&Config->implement_lists,0,sizeof(Config->implement_lists)); + + printf("\n"); + + this->Res = new DNS(this); + + this->LoadAllModules(); + /* Just in case no modules were loaded - fix for bug #101 */ + this->BuildISupport(); + InitializeDisabledCommands(Config->DisabledCommands, this); + + if ((Config->ports.size() == 0) && (found_ports > 0)) + { + printf("\nERROR: I couldn't bind any ports! Are you sure you didn't start InspIRCd twice?\n"); + Log(DEFAULT,"ERROR: I couldn't bind any ports! Are you sure you didn't start InspIRCd twice?"); + Exit(EXIT_STATUS_BIND); + } + + if (Config->ports.size() != (unsigned int)found_ports) + { + printf("\nWARNING: Not all your client ports could be bound --\nstarting anyway with %d of %d client ports bound.\n\n", bounditems, found_ports); + printf("The following port(s) failed to bind:\n"); + int j = 1; + for (FailedPortList::iterator i = pl.begin(); i != pl.end(); i++, j++) + { + printf("%d.\tIP: %s\tPort: %lu\n", j, i->first.empty() ? "<all>" : i->first.c_str(), (unsigned long)i->second); + } + } +#ifndef WINDOWS + if (!Config->nofork) + { + if (kill(getppid(), SIGTERM) == -1) + { + printf("Error killing parent process: %s\n",strerror(errno)); + Log(DEFAULT,"Error killing parent process: %s",strerror(errno)); + } + } + + if (isatty(0) && isatty(1) && isatty(2)) + { + /* We didn't start from a TTY, we must have started from a background process - + * e.g. we are restarting, or being launched by cron. Dont kill parent, and dont + * close stdin/stdout + */ + if (!do_nofork) + { + fclose(stdin); + fclose(stderr); + fclose(stdout); + } + else + { + Log(DEFAULT,"Keeping pseudo-tty open as we are running in the foreground."); + } + } +#else + InitIPC(); + if(!Config->nofork) + { + WindowsForkKillOwner(this); + FreeConsole(); + } +#endif + printf("\nInspIRCd is now running!\n"); + Log(DEFAULT,"Startup complete."); + + this->WritePID(Config->PID); +} + +std::string InspIRCd::GetVersionString() +{ + char versiondata[MAXBUF]; + char dnsengine[] = "singlethread-object"; + + if (*Config->CustomVersion) + { + snprintf(versiondata,MAXBUF,"%s %s :%s",VERSION,Config->ServerName,Config->CustomVersion); + } + else + { + snprintf(versiondata,MAXBUF,"%s %s :%s [FLAGS=%s,%s,%s]",VERSION,Config->ServerName,SYSTEM,REVISION,SE->GetName().c_str(),dnsengine); + } + return versiondata; +} + +char* InspIRCd::ModuleError() +{ + return MODERR; +} + +void InspIRCd::EraseFactory(int j) +{ + int v = 0; + for (std::vector<ircd_module*>::iterator t = factory.begin(); t != factory.end(); t++) + { + if (v == j) + { + delete *t; + factory.erase(t); + factory.push_back(NULL); + return; + } + v++; + } +} + +void InspIRCd::EraseModule(int j) +{ + int v1 = 0; + for (ModuleList::iterator m = modules.begin(); m!= modules.end(); m++) + { + if (v1 == j) + { + DELETE(*m); + modules.erase(m); + modules.push_back(NULL); + break; + } + v1++; + } + int v2 = 0; + for (std::vector<std::string>::iterator v = Config->module_names.begin(); v != Config->module_names.end(); v++) + { + if (v2 == j) + { + Config->module_names.erase(v); + break; + } + v2++; + } + +} + +void InspIRCd::MoveTo(std::string modulename,int slot) +{ + unsigned int v2 = 256; + for (unsigned int v = 0; v < Config->module_names.size(); v++) + { + if (Config->module_names[v] == modulename) + { + // found an instance, swap it with the item at the end + v2 = v; + break; + } + } + if ((v2 != (unsigned int)slot) && (v2 < 256)) + { + // Swap the module names over + Config->module_names[v2] = Config->module_names[slot]; + Config->module_names[slot] = modulename; + // now swap the module factories + ircd_module* temp = factory[v2]; + factory[v2] = factory[slot]; + factory[slot] = temp; + // now swap the module objects + Module* temp_module = modules[v2]; + modules[v2] = modules[slot]; + modules[slot] = temp_module; + // now swap the implement lists (we dont + // need to swap the global or recount it) + for (int n = 0; n < 255; n++) + { + char x = Config->implement_lists[v2][n]; + Config->implement_lists[v2][n] = Config->implement_lists[slot][n]; + Config->implement_lists[slot][n] = x; + } + } +} + +void InspIRCd::MoveAfter(std::string modulename, std::string after) +{ + for (unsigned int v = 0; v < Config->module_names.size(); v++) + { + if (Config->module_names[v] == after) + { + MoveTo(modulename, v); + return; + } + } +} + +void InspIRCd::MoveBefore(std::string modulename, std::string before) +{ + for (unsigned int v = 0; v < Config->module_names.size(); v++) + { + if (Config->module_names[v] == before) + { + if (v > 0) + { + MoveTo(modulename, v-1); + } + else + { + MoveTo(modulename, v); + } + return; + } + } +} + +void InspIRCd::MoveToFirst(std::string modulename) +{ + MoveTo(modulename,0); +} + +void InspIRCd::MoveToLast(std::string modulename) +{ + MoveTo(modulename,this->GetModuleCount()); +} + +void InspIRCd::BuildISupport() +{ + // the neatest way to construct the initial 005 numeric, considering the number of configure constants to go in it... + std::stringstream v; + v << "WALLCHOPS WALLVOICES MODES=" << MAXMODES-1 << " CHANTYPES=# PREFIX=" << this->Modes->BuildPrefixes() << " MAP MAXCHANNELS=" << Config->MaxChans << " MAXBANS=60 VBANLIST NICKLEN=" << NICKMAX-1; + v << " CASEMAPPING=rfc1459 STATUSMSG=@%+ CHARSET=ascii TOPICLEN=" << MAXTOPIC << " KICKLEN=" << MAXKICK << " MAXTARGETS=" << Config->MaxTargets << " AWAYLEN="; + v << MAXAWAY << " CHANMODES=" << this->Modes->ChanModes() << " FNC NETWORK=" << Config->Network << " MAXPARA=32 ELIST=MU"; + Config->data005 = v.str(); + FOREACH_MOD_I(this,I_On005Numeric,On005Numeric(Config->data005)); + Config->Update005(); +} + +bool InspIRCd::UnloadModule(const char* filename) +{ + std::string filename_str = filename; + for (unsigned int j = 0; j != Config->module_names.size(); j++) + { + if (Config->module_names[j] == filename_str) + { + if (modules[j]->GetVersion().Flags & VF_STATIC) + { + this->Log(DEFAULT,"Failed to unload STATIC module %s",filename); + snprintf(MODERR,MAXBUF,"Module not unloadable (marked static)"); + return false; + } + std::pair<int,std::string> intercount = GetInterfaceInstanceCount(modules[j]); + if (intercount.first > 0) + { + this->Log(DEFAULT,"Failed to unload module %s, being used by %d other(s) via interface '%s'",filename, intercount.first, intercount.second.c_str()); + snprintf(MODERR,MAXBUF,"Module not unloadable (Still in use by %d other module%s which %s using its interface '%s') -- unload dependent modules first!", + intercount.first, + intercount.first > 1 ? "s" : "", + intercount.first > 1 ? "are" : "is", + intercount.second.c_str()); + return false; + } + /* Give the module a chance to tidy out all its metadata */ + for (chan_hash::iterator c = this->chanlist->begin(); c != this->chanlist->end(); c++) + { + modules[j]->OnCleanup(TYPE_CHANNEL,c->second); + } + for (user_hash::iterator u = this->clientlist->begin(); u != this->clientlist->end(); u++) + { + modules[j]->OnCleanup(TYPE_USER,u->second); + } + + /* Tidy up any dangling resolvers */ + this->Res->CleanResolvers(modules[j]); + + FOREACH_MOD_I(this,I_OnUnloadModule,OnUnloadModule(modules[j],Config->module_names[j])); + + for(int t = 0; t < 255; t++) + { + Config->global_implementation[t] -= Config->implement_lists[j][t]; + } + + /* We have to renumber implement_lists after unload because the module numbers change! + */ + for(int j2 = j; j2 < 254; j2++) + { + for(int t = 0; t < 255; t++) + { + Config->implement_lists[j2][t] = Config->implement_lists[j2+1][t]; + } + } + + // found the module + Parser->RemoveCommands(filename); + this->EraseModule(j); + this->EraseFactory(j); + this->Log(DEFAULT,"Module %s unloaded",filename); + this->ModCount--; + BuildISupport(); + return true; + } + } + this->Log(DEFAULT,"Module %s is not loaded, cannot unload it!",filename); + snprintf(MODERR,MAXBUF,"Module not loaded"); + return false; +} + +bool InspIRCd::LoadModule(const char* filename) +{ + /* Do we have a glob pattern in the filename? + * The user wants to load multiple modules which + * match the pattern. + */ + if (strchr(filename,'*') || (strchr(filename,'?'))) + { + int n_match = 0; + DIR* library = opendir(Config->ModPath); + if (library) + { + /* Try and locate and load all modules matching the pattern */ + dirent* entry = NULL; + while ((entry = readdir(library))) + { + if (this->MatchText(entry->d_name, filename)) + { + if (!this->LoadModule(entry->d_name)) + n_match++; + } + } + closedir(library); + } + /* Loadmodule will now return false if any one of the modules failed + * to load (but wont abort when it encounters a bad one) and when 1 or + * more modules were actually loaded. + */ + return (n_match > 0); + } + + char modfile[MAXBUF]; + snprintf(modfile,MAXBUF,"%s/%s",Config->ModPath,filename); + std::string filename_str = filename; + + if (!ServerConfig::DirValid(modfile)) + { + this->Log(DEFAULT,"Module %s is not within the modules directory.",modfile); + snprintf(MODERR,MAXBUF,"Module %s is not within the modules directory.",modfile); + return false; + } + if (ServerConfig::FileExists(modfile)) + { + + for (unsigned int j = 0; j < Config->module_names.size(); j++) + { + if (Config->module_names[j] == filename_str) + { + this->Log(DEFAULT,"Module %s is already loaded, cannot load a module twice!",modfile); + snprintf(MODERR,MAXBUF,"Module already loaded"); + return false; + } + } + Module* m = NULL; + ircd_module* a = NULL; + try + { + a = new ircd_module(this, modfile); + factory[this->ModCount+1] = a; + if (factory[this->ModCount+1]->LastError()) + { + this->Log(DEFAULT,"Unable to load %s: %s",modfile,factory[this->ModCount+1]->LastError()); + snprintf(MODERR,MAXBUF,"Loader/Linker error: %s",factory[this->ModCount+1]->LastError()); + return false; + } + if ((long)factory[this->ModCount+1]->factory != -1) + { + m = factory[this->ModCount+1]->factory->CreateModule(this); + + Version v = m->GetVersion(); + + if (v.API != API_VERSION) + { + delete m; + this->Log(DEFAULT,"Unable to load %s: Incorrect module API version: %d (our version: %d)",modfile,v.API,API_VERSION); + snprintf(MODERR,MAXBUF,"Loader/Linker error: Incorrect module API version: %d (our version: %d)",v.API,API_VERSION); + return false; + } + else + { + this->Log(DEFAULT,"New module introduced: %s (API version %d, Module version %d.%d.%d.%d)%s", filename, v.API, v.Major, v.Minor, v.Revision, v.Build, (!(v.Flags & VF_VENDOR) ? " [3rd Party]" : " [Vendor]")); + } + + modules[this->ModCount+1] = m; + /* save the module and the module's classfactory, if + * this isnt done, random crashes can occur :/ */ + Config->module_names.push_back(filename); + + char* x = &Config->implement_lists[this->ModCount+1][0]; + for(int t = 0; t < 255; t++) + x[t] = 0; + + modules[this->ModCount+1]->Implements(x); + + for(int t = 0; t < 255; t++) + Config->global_implementation[t] += Config->implement_lists[this->ModCount+1][t]; + } + else + { + this->Log(DEFAULT,"Unable to load %s",modfile); + snprintf(MODERR,MAXBUF,"Factory function failed: Probably missing init_module() entrypoint."); + return false; + } + } + catch (CoreException& modexcept) + { + this->Log(DEFAULT,"Unable to load %s: %s",modfile,modexcept.GetReason()); + snprintf(MODERR,MAXBUF,"Factory function of %s threw an exception: %s", modexcept.GetSource(), modexcept.GetReason()); + return false; + } + } + else + { + this->Log(DEFAULT,"InspIRCd: startup: Module Not Found %s",modfile); + snprintf(MODERR,MAXBUF,"Module file could not be found"); + return false; + } + this->ModCount++; + FOREACH_MOD_I(this,I_OnLoadModule,OnLoadModule(modules[this->ModCount],filename_str)); + // now work out which modules, if any, want to move to the back of the queue, + // and if they do, move them there. + std::vector<std::string> put_to_back; + std::vector<std::string> put_to_front; + std::map<std::string,std::string> put_before; + std::map<std::string,std::string> put_after; + for (unsigned int j = 0; j < Config->module_names.size(); j++) + { + if (modules[j]->Prioritize() == PRIORITY_LAST) + put_to_back.push_back(Config->module_names[j]); + else if (modules[j]->Prioritize() == PRIORITY_FIRST) + put_to_front.push_back(Config->module_names[j]); + else if ((modules[j]->Prioritize() & 0xFF) == PRIORITY_BEFORE) + put_before[Config->module_names[j]] = Config->module_names[modules[j]->Prioritize() >> 8]; + else if ((modules[j]->Prioritize() & 0xFF) == PRIORITY_AFTER) + put_after[Config->module_names[j]] = Config->module_names[modules[j]->Prioritize() >> 8]; + } + for (unsigned int j = 0; j < put_to_back.size(); j++) + MoveToLast(put_to_back[j]); + for (unsigned int j = 0; j < put_to_front.size(); j++) + MoveToFirst(put_to_front[j]); + for (std::map<std::string,std::string>::iterator j = put_before.begin(); j != put_before.end(); j++) + MoveBefore(j->first,j->second); + for (std::map<std::string,std::string>::iterator j = put_after.begin(); j != put_after.end(); j++) + MoveAfter(j->first,j->second); + BuildISupport(); + return true; +} + +void InspIRCd::DoOneIteration(bool process_module_sockets) +{ +#ifndef WIN32 + static rusage ru; +#else + static time_t uptime; + static struct tm * stime; + static char window_title[100]; +#endif + + /* time() seems to be a pretty expensive syscall, so avoid calling it too much. + * Once per loop iteration is pleanty. + */ + OLDTIME = TIME; + TIME = time(NULL); + + /* Run background module timers every few seconds + * (the docs say modules shouldnt rely on accurate + * timing using this event, so we dont have to + * time this exactly). + */ + if (TIME != OLDTIME) + { + if (TIME < OLDTIME) + WriteOpers("*** \002EH?!\002 -- Time is flowing BACKWARDS in this dimension! Clock drifted backwards %d secs.",abs(OLDTIME-TIME)); + if ((TIME % 3600) == 0) + { + this->RehashUsersAndChans(); + FOREACH_MOD_I(this, I_OnGarbageCollect, OnGarbageCollect()); + } + Timers->TickTimers(TIME); + this->DoBackgroundUserStuff(TIME); + + if ((TIME % 5) == 0) + { + XLines->expire_lines(); + FOREACH_MOD_I(this,I_OnBackgroundTimer,OnBackgroundTimer(TIME)); + Timers->TickMissedTimers(TIME); + } +#ifndef WIN32 + /* Same change as in cmd_stats.cpp, use RUSAGE_SELF rather than '0' -- Om */ + if (!getrusage(RUSAGE_SELF, &ru)) + { + gettimeofday(&this->stats->LastSampled, NULL); + this->stats->LastCPU = ru.ru_utime; + } +#else + CheckIPC(this); + + if(Config->nofork) + { + uptime = Time() - startup_time; + stime = gmtime(&uptime); + snprintf(window_title, 100, "InspIRCd - %u clients, %u accepted connections - Up %u days, %.2u:%.2u:%.2u", + LocalUserCount(), stats->statsAccept, stime->tm_yday, stime->tm_hour, stime->tm_min, stime->tm_sec); + SetConsoleTitle(window_title); + } +#endif + } + + /* Call the socket engine to wait on the active + * file descriptors. The socket engine has everything's + * descriptors in its list... dns, modules, users, + * servers... so its nice and easy, just one call. + * This will cause any read or write events to be + * dispatched to their handlers. + */ + SE->DispatchEvents(); + + /* if any users was quit, take them out */ + GlobalCulls.Apply(); + + /* If any inspsockets closed, remove them */ + for (std::map<InspSocket*,InspSocket*>::iterator x = SocketCull.begin(); x != SocketCull.end(); ++x) + { + SE->DelFd(x->second); + x->second->Close(); + delete x->second; + } + SocketCull.clear(); +} + +int InspIRCd::Run() +{ + while (true) + { + DoOneIteration(true); + } + /* This is never reached -- we hope! */ + return 0; +} + +/**********************************************************************************/ + +/** + * An ircd in four lines! bwahahaha. ahahahahaha. ahahah *cough*. + */ + +int main(int argc, char** argv) +{ + SI = new InspIRCd(argc, argv); + SI->Run(); + delete SI; + return 0; +} + +/* this returns true when all modules are satisfied that the user should be allowed onto the irc server + * (until this returns true, a user will block in the waiting state, waiting to connect up to the + * registration timeout maximum seconds) + */ +bool InspIRCd::AllModulesReportReady(userrec* user) +{ + if (!Config->global_implementation[I_OnCheckReady]) + return true; + + for (int i = 0; i <= this->GetModuleCount(); i++) + { + if (Config->implement_lists[i][I_OnCheckReady]) + { + int res = modules[i]->OnCheckReady(user); + if (!res) + return false; + } + } + return true; +} + +int InspIRCd::GetModuleCount() +{ + return this->ModCount; +} + +time_t InspIRCd::Time(bool delta) +{ + if (delta) + return TIME + time_delta; + return TIME; +} + +int InspIRCd::SetTimeDelta(int delta) +{ + int old = time_delta; + time_delta = delta; + this->Log(DEBUG, "Time delta set to %d (was %d)", time_delta, old); + return old; +} + +void InspIRCd::AddLocalClone(userrec* user) +{ + clonemap::iterator x = local_clones.find(user->GetIPString()); + if (x != local_clones.end()) + x->second++; + else + local_clones[user->GetIPString()] = 1; +} + +void InspIRCd::AddGlobalClone(userrec* user) +{ + clonemap::iterator y = global_clones.find(user->GetIPString()); + if (y != global_clones.end()) + y->second++; + else + global_clones[user->GetIPString()] = 1; +} + +int InspIRCd::GetTimeDelta() +{ + return time_delta; +} + +bool FileLogger::Readable() +{ + return false; +} + +void FileLogger::HandleEvent(EventType et, int errornum) +{ + this->WriteLogLine(""); + if (log) + ServerInstance->SE->DelFd(this); +} + +void FileLogger::WriteLogLine(const std::string &line) +{ + if (line.length()) + buffer.append(line); + + if (log) + { + int written = fprintf(log,"%s",buffer.c_str()); +#ifdef WINDOWS + buffer.clear(); +#else + if ((written >= 0) && (written < (int)buffer.length())) + { + buffer.erase(0, buffer.length()); + ServerInstance->SE->AddFd(this); + } + else if (written == -1) + { + if (errno == EAGAIN) + ServerInstance->SE->AddFd(this); + } + else + { + /* Wrote the whole buffer, and no need for write callback */ + buffer.clear(); + } +#endif + if (writeops++ % 20) + { + fflush(log); + } + } +} + +void FileLogger::Close() +{ + if (log) + { + /* Burlex: Windows assumes nonblocking on FILE* pointers anyway, and also "file" fd's aren't the same + * as socket fd's. */ +#ifndef WIN32 + int flags = fcntl(fileno(log), F_GETFL, 0); + fcntl(fileno(log), F_SETFL, flags ^ O_NONBLOCK); +#endif + if (buffer.size()) + fprintf(log,"%s",buffer.c_str()); + +#ifndef WINDOWS + ServerInstance->SE->DelFd(this); +#endif + + fflush(log); + fclose(log); + } + + buffer.clear(); +} + +FileLogger::FileLogger(InspIRCd* Instance, FILE* logfile) : ServerInstance(Instance), log(logfile), writeops(0) +{ + if (log) + { + irc::sockets::NonBlocking(fileno(log)); + this->SetFd(fileno(log)); + buffer.clear(); + } +} + +FileLogger::~FileLogger() +{ + this->Close(); +} + diff --git a/src/inspsocket.cpp b/src/inspsocket.cpp index a2c5cc47f..f29366c73 100644 --- a/src/inspsocket.cpp +++ b/src/inspsocket.cpp @@ -1 +1,750 @@ -/* +------------------------------------+
* | 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 "socket.h"
#include "configreader.h"
#include "inspstring.h"
#include "socketengine.h"
#include "inspircd.h"
using irc::sockets::OpenTCPSocket;
bool InspSocket::Readable()
{
return ((this->state != I_CONNECTING) && (this->WaitingForWriteEvent == false));
}
InspSocket::InspSocket(InspIRCd* SI)
{
this->Timeout = NULL;
this->state = I_DISCONNECTED;
this->fd = -1;
this->WaitingForWriteEvent = false;
this->Instance = SI;
this->IsIOHooked = false;
}
InspSocket::InspSocket(InspIRCd* SI, int newfd, const char* ip)
{
this->Timeout = NULL;
this->fd = newfd;
this->state = I_CONNECTED;
strlcpy(this->IP,ip,MAXBUF);
this->WaitingForWriteEvent = false;
this->Instance = SI;
this->IsIOHooked = false;
if (this->fd > -1)
this->Instance->SE->AddFd(this);
}
InspSocket::InspSocket(InspIRCd* SI, const std::string &ipaddr, int aport, bool listening, unsigned long maxtime, const std::string &connectbindip)
{
this->cbindip = connectbindip;
this->fd = -1;
this->Instance = SI;
strlcpy(host,ipaddr.c_str(),MAXBUF);
this->WaitingForWriteEvent = false;
this->IsIOHooked = false;
this->Timeout = NULL;
if (listening)
{
if ((this->fd = OpenTCPSocket(host)) == ERROR)
{
this->fd = -1;
this->state = I_ERROR;
this->OnError(I_ERR_SOCKET);
return;
}
else
{
if (!SI->BindSocket(this->fd,aport,(char*)ipaddr.c_str()))
{
this->Close();
this->fd = -1;
this->state = I_ERROR;
this->OnError(I_ERR_BIND);
this->ClosePending = true;
return;
}
else
{
this->state = I_LISTENING;
this->port = aport;
if (this->fd > -1)
{
if (!this->Instance->SE->AddFd(this))
{
this->Close();
this->state = I_ERROR;
this->OnError(I_ERR_NOMOREFDS);
}
}
return;
}
}
}
else
{
strlcpy(this->host,ipaddr.c_str(),MAXBUF);
this->port = aport;
bool ipvalid = true;
#ifdef IPV6
if (strchr(host,':'))
{
in6_addr n;
if (inet_pton(AF_INET6, host, &n) < 1)
ipvalid = false;
}
else
#endif
{
in_addr n;
if (inet_aton(host,&n) < 1)
ipvalid = false;
}
if (!ipvalid)
{
this->Instance->Log(DEBUG,"BUG: Hostname passed to InspSocket, rather than an IP address!");
this->OnError(I_ERR_CONNECT);
this->Close();
this->fd = -1;
this->state = I_ERROR;
return;
}
else
{
strlcpy(this->IP,host,MAXBUF);
timeout_val = maxtime;
if (!this->DoConnect())
{
this->OnError(I_ERR_CONNECT);
this->Close();
this->fd = -1;
this->state = I_ERROR;
return;
}
}
}
}
void InspSocket::WantWrite()
{
this->Instance->SE->WantWrite(this);
this->WaitingForWriteEvent = true;
}
void InspSocket::SetQueues(int nfd)
{
// attempt to increase socket sendq and recvq as high as its possible
int sendbuf = 32768;
int recvbuf = 32768;
setsockopt(nfd,SOL_SOCKET,SO_SNDBUF,(const char *)&sendbuf,sizeof(sendbuf));
setsockopt(nfd,SOL_SOCKET,SO_RCVBUF,(const char *)&recvbuf,sizeof(sendbuf));
}
/* Most irc servers require you to specify the ip you want to bind to.
* If you dont specify an IP, they rather dumbly bind to the first IP
* of the box (e.g. INADDR_ANY). In InspIRCd, we scan thought the IP
* addresses we've bound server ports to, and we try and bind our outbound
* connections to the first usable non-loopback and non-any IP we find.
* This is easier to configure when you have a lot of links and a lot
* of servers to configure.
*/
bool InspSocket::BindAddr(const std::string &ip)
{
ConfigReader Conf(this->Instance);
socklen_t size = sizeof(sockaddr_in);
#ifdef IPV6
bool v6 = false;
/* Are we looking for a binding to fit an ipv6 host? */
if ((ip.empty()) || (ip.find(':') != std::string::npos))
v6 = true;
#endif
int j = 0;
while (j < Conf.Enumerate("bind") || (!ip.empty()))
{
std::string IP = ip.empty() ? Conf.ReadValue("bind","address",j) : ip;
if (!ip.empty() || Conf.ReadValue("bind","type",j) == "servers")
{
if (!ip.empty() || ((IP != "*") && (IP != "127.0.0.1") && (!IP.empty()) && (IP != "::1")))
{
sockaddr* s = new sockaddr[2];
#ifdef IPV6
if (v6)
{
in6_addr n;
if (inet_pton(AF_INET6, IP.c_str(), &n) > 0)
{
memcpy(&((sockaddr_in6*)s)->sin6_addr, &n, sizeof(n));
((sockaddr_in6*)s)->sin6_port = 0;
((sockaddr_in6*)s)->sin6_family = AF_INET6;
size = sizeof(sockaddr_in6);
}
else
{
delete[] s;
j++;
continue;
}
}
else
#endif
{
in_addr n;
if (inet_aton(IP.c_str(), &n) > 0)
{
((sockaddr_in*)s)->sin_addr = n;
((sockaddr_in*)s)->sin_port = 0;
((sockaddr_in*)s)->sin_family = AF_INET;
}
else
{
delete[] s;
j++;
continue;
}
}
if (bind(this->fd, s, size) < 0)
{
this->state = I_ERROR;
this->OnError(I_ERR_BIND);
this->fd = -1;
delete[] s;
return false;
}
delete[] s;
return true;
}
}
j++;
}
return true;
}
bool InspSocket::DoConnect()
{
sockaddr* addr = new sockaddr[2];
socklen_t size = sizeof(sockaddr_in);
#ifdef IPV6
bool v6 = false;
if ((!*this->host) || strchr(this->host, ':'))
v6 = true;
if (v6)
{
this->fd = socket(AF_INET6, SOCK_STREAM, 0);
if ((this->fd > -1) && ((strstr(this->IP,"::ffff:") != (char*)&this->IP) && (strstr(this->IP,"::FFFF:") != (char*)&this->IP)))
{
if (!this->BindAddr(this->cbindip))
{
delete[] addr;
return false;
}
}
}
else
#endif
{
this->fd = socket(AF_INET, SOCK_STREAM, 0);
if (this->fd > -1)
{
if (!this->BindAddr(this->cbindip))
{
delete[] addr;
return false;
}
}
}
if (this->fd == -1)
{
this->state = I_ERROR;
this->OnError(I_ERR_SOCKET);
delete[] addr;
return false;
}
#ifdef IPV6
if (v6)
{
in6_addr addy;
if (inet_pton(AF_INET6, this->host, &addy) > 0)
{
((sockaddr_in6*)addr)->sin6_family = AF_INET6;
memcpy(&((sockaddr_in6*)addr)->sin6_addr, &addy, sizeof(addy));
((sockaddr_in6*)addr)->sin6_port = htons(this->port);
size = sizeof(sockaddr_in6);
}
}
else
#endif
{
in_addr addy;
if (inet_aton(this->host, &addy) > 0)
{
((sockaddr_in*)addr)->sin_family = AF_INET;
((sockaddr_in*)addr)->sin_addr = addy;
((sockaddr_in*)addr)->sin_port = htons(this->port);
}
}
#ifndef WIN32
int flags = fcntl(this->fd, F_GETFL, 0);
fcntl(this->fd, F_SETFL, flags | O_NONBLOCK);
#else
unsigned long flags = 0;
ioctlsocket(this->fd, FIONBIO, &flags);
#endif
if (connect(this->fd, (sockaddr*)addr, size) == -1)
{
if (errno != EINPROGRESS)
{
this->OnError(I_ERR_CONNECT);
this->Close();
this->state = I_ERROR;
return false;
}
this->Timeout = new SocketTimeout(this->GetFd(), this->Instance, this, timeout_val, this->Instance->Time());
this->Instance->Timers->AddTimer(this->Timeout);
}
this->state = I_CONNECTING;
if (this->fd > -1)
{
if (!this->Instance->SE->AddFd(this))
{
this->OnError(I_ERR_NOMOREFDS);
this->Close();
this->state = I_ERROR;
return false;
}
this->SetQueues(this->fd);
}
return true;
}
void InspSocket::Close()
{
/* Save this, so we dont lose it,
* otherise on failure, error messages
* might be inaccurate.
*/
int save = errno;
if (this->fd > -1)
{
if (this->IsIOHooked && Instance->Config->GetIOHook(this))
{
try
{
Instance->Config->GetIOHook(this)->OnRawSocketClose(this->fd);
}
catch (CoreException& modexcept)
{
Instance->Log(DEFAULT,"%s threw an exception: %s", modexcept.GetSource(), modexcept.GetReason());
}
}
this->OnClose();
shutdown(this->fd,2);
close(this->fd);
if (Instance->SocketCull.find(this) == Instance->SocketCull.end())
Instance->SocketCull[this] = this;
}
errno = save;
}
std::string InspSocket::GetIP()
{
return this->IP;
}
char* InspSocket::Read()
{
#ifdef WINDOWS
if ((fd < 0) || (m_internalFd > MAX_DESCRIPTORS))
#else
if ((fd < 0) || (fd > MAX_DESCRIPTORS))
#endif
return NULL;
int n = 0;
if (this->IsIOHooked)
{
int result2 = 0;
int MOD_RESULT = 0;
try
{
MOD_RESULT = Instance->Config->GetIOHook(this)->OnRawSocketRead(this->fd,this->ibuf,sizeof(this->ibuf),result2);
}
catch (CoreException& modexcept)
{
Instance->Log(DEFAULT,"%s threw an exception: %s", modexcept.GetSource(), modexcept.GetReason());
}
if (MOD_RESULT < 0)
{
n = -1;
errno = EAGAIN;
}
else
{
n = result2;
}
}
else
{
n = recv(this->fd,this->ibuf,sizeof(this->ibuf),0);
}
if ((n > 0) && (n <= (int)sizeof(this->ibuf)))
{
ibuf[n] = 0;
return ibuf;
}
else
{
int err = errno;
if (err == EAGAIN)
return "";
else
return NULL;
}
}
void InspSocket::MarkAsClosed()
{
}
// There are two possible outcomes to this function.
// It will either write all of the data, or an undefined amount.
// If an undefined amount is written the connection has failed
// and should be aborted.
int InspSocket::Write(const std::string &data)
{
/* Try and append the data to the back of the queue, and send it on its way
*/
outbuffer.push_back(data);
this->Instance->SE->WantWrite(this);
return (!this->FlushWriteBuffer());
}
bool InspSocket::FlushWriteBuffer()
{
errno = 0;
if ((this->fd > -1) && (this->state == I_CONNECTED))
{
if (this->IsIOHooked)
{
while (outbuffer.size() && (errno != EAGAIN))
{
try
{
/* XXX: The lack of buffering here is NOT a bug, modules implementing this interface have to
* implement their own buffering mechanisms
*/
Instance->Config->GetIOHook(this)->OnRawSocketWrite(this->fd, outbuffer[0].c_str(), outbuffer[0].length());
outbuffer.pop_front();
}
catch (CoreException& modexcept)
{
Instance->Log(DEBUG,"%s threw an exception: %s", modexcept.GetSource(), modexcept.GetReason());
return true;
}
}
}
else
{
/* If we have multiple lines, try to send them all,
* not just the first one -- Brain
*/
while (outbuffer.size() && (errno != EAGAIN))
{
/* Send a line */
#ifndef WIN32
int result = write(this->fd,outbuffer[0].c_str(),outbuffer[0].length());
#else
int result = send(this->fd,outbuffer[0].c_str(),outbuffer[0].length(), 0);
#endif
if (result > 0)
{
if ((unsigned int)result >= outbuffer[0].length())
{
/* The whole block was written (usually a line)
* Pop the block off the front of the queue,
* dont set errno, because we are clear of errors
* and want to try and write the next block too.
*/
outbuffer.pop_front();
}
else
{
std::string temp = outbuffer[0].substr(result);
outbuffer[0] = temp;
/* We didnt get the whole line out. arses.
* Try again next time, i guess. Set errno,
* because we shouldnt be writing any more now,
* until the socketengine says its safe to do so.
*/
errno = EAGAIN;
}
}
else if ((result == -1) && (errno != EAGAIN))
{
this->OnError(I_ERR_WRITE);
this->state = I_ERROR;
this->Instance->SE->DelFd(this);
this->Close();
return true;
}
}
}
}
if ((errno == EAGAIN) && (fd > -1))
{
this->Instance->SE->WantWrite(this);
}
return (fd < 0);
}
void SocketTimeout::Tick(time_t now)
{
if (ServerInstance->SE->GetRef(this->sfd) != this->sock)
return;
if (this->sock->state == I_CONNECTING)
{
// for non-listening sockets, the timeout can occur
// which causes termination of the connection after
// the given number of seconds without a successful
// connection.
this->sock->OnTimeout();
this->sock->OnError(I_ERR_TIMEOUT);
this->sock->timeout = true;
/* NOTE: We must set this AFTER DelFd, as we added
* this socket whilst writeable. This means that we
* must DELETE the socket whilst writeable too!
*/
this->sock->state = I_ERROR;
if (ServerInstance->SocketCull.find(this->sock) == ServerInstance->SocketCull.end())
ServerInstance->SocketCull[this->sock] = this->sock;
}
this->sock->Timeout = NULL;
}
bool InspSocket::Poll()
{
#ifdef WINDOWS
if(Instance->SE->GetRef(this->fd) != this)
return false;
int incoming = -1;
#else
if (this->Instance->SE->GetRef(this->fd) != this)
return false;
int incoming = -1;
if ((fd < 0) || (fd > MAX_DESCRIPTORS))
return false;
#endif
switch (this->state)
{
case I_CONNECTING:
/* Our socket was in write-state, so delete it and re-add it
* in read-state.
*/
#ifndef WINDOWS
if (this->fd > -1)
{
this->Instance->SE->DelFd(this);
this->SetState(I_CONNECTED);
if (!this->Instance->SE->AddFd(this))
return false;
}
#else
this->SetState(I_CONNECTED);
#endif
Instance->Log(DEBUG,"Inspsocket I_CONNECTING state");
if (Instance->Config->GetIOHook(this))
{
Instance->Log(DEBUG,"Hook for raw connect");
try
{
Instance->Config->GetIOHook(this)->OnRawSocketConnect(this->fd);
}
catch (CoreException& modexcept)
{
Instance->Log(DEBUG,"%s threw an exception: %s", modexcept.GetSource(), modexcept.GetReason());
}
}
return this->OnConnected();
break;
case I_LISTENING:
{
sockaddr* client = new sockaddr[2];
length = sizeof (sockaddr_in);
std::string recvip;
#ifdef IPV6
if ((!*this->host) || strchr(this->host, ':'))
length = sizeof(sockaddr_in6);
#endif
incoming = _accept (this->fd, client, &length);
#ifdef IPV6
if ((!*this->host) || strchr(this->host, ':'))
{
char buf[1024];
recvip = inet_ntop(AF_INET6, &((sockaddr_in6*)client)->sin6_addr, buf, sizeof(buf));
}
else
#endif
recvip = inet_ntoa(((sockaddr_in*)client)->sin_addr);
this->OnIncomingConnection(incoming, (char*)recvip.c_str());
if (this->IsIOHooked)
{
try
{
Instance->Config->GetIOHook(this)->OnRawSocketAccept(incoming, recvip.c_str(), this->port);
}
catch (CoreException& modexcept)
{
Instance->Log(DEBUG,"%s threw an exception: %s", modexcept.GetSource(), modexcept.GetReason());
}
}
this->SetQueues(incoming);
delete[] client;
return true;
}
break;
case I_CONNECTED:
/* Process the read event */
return this->OnDataReady();
break;
default:
break;
}
return true;
}
void InspSocket::SetState(InspSocketState s)
{
this->state = s;
}
InspSocketState InspSocket::GetState()
{
return this->state;
}
int InspSocket::GetFd()
{
return this->fd;
}
bool InspSocket::OnConnected() { return true; }
void InspSocket::OnError(InspSocketError e) { return; }
int InspSocket::OnDisconnect() { return 0; }
int InspSocket::OnIncomingConnection(int newfd, char* ip) { return 0; }
bool InspSocket::OnDataReady() { return true; }
bool InspSocket::OnWriteReady() { return true; }
void InspSocket::OnTimeout() { return; }
void InspSocket::OnClose() { return; }
InspSocket::~InspSocket()
{
this->Close();
if (Timeout)
{
Instance->Timers->DelTimer(Timeout);
Timeout = NULL;
}
}
void InspSocket::HandleEvent(EventType et, int errornum)
{
switch (et)
{
case EVENT_ERROR:
switch (errornum)
{
case ETIMEDOUT:
this->OnError(I_ERR_TIMEOUT);
break;
case ECONNREFUSED:
case 0:
this->OnError(this->state == I_CONNECTING ? I_ERR_CONNECT : I_ERR_WRITE);
break;
case EADDRINUSE:
this->OnError(I_ERR_BIND);
break;
case EPIPE:
case EIO:
this->OnError(I_ERR_WRITE);
break;
}
if (this->Instance->SocketCull.find(this) == this->Instance->SocketCull.end())
this->Instance->SocketCull[this] = this;
return;
break;
case EVENT_READ:
if (!this->Poll())
{
if (this->Instance->SocketCull.find(this) == this->Instance->SocketCull.end())
this->Instance->SocketCull[this] = this;
return;
}
break;
case EVENT_WRITE:
if (this->WaitingForWriteEvent)
{
this->WaitingForWriteEvent = false;
if (!this->OnWriteReady())
{
if (this->Instance->SocketCull.find(this) == this->Instance->SocketCull.end())
this->Instance->SocketCull[this] = this;
return;
}
}
if (this->state == I_CONNECTING)
{
/* This might look wrong as if we should be actually calling
* with EVENT_WRITE, but trust me it is correct. There are some
* writeability-state things in the read code, because of how
* InspSocket used to work regarding write buffering in previous
* versions of InspIRCd. - Brain
*/
this->HandleEvent(EVENT_READ);
return;
}
else
{
if (this->FlushWriteBuffer())
{
if (this->Instance->SocketCull.find(this) == this->Instance->SocketCull.end())
this->Instance->SocketCull[this] = this;
return;
}
}
break;
}
}
\ No newline at end of file +/* +------------------------------------+ + * | 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 "socket.h" +#include "configreader.h" +#include "inspstring.h" +#include "socketengine.h" +#include "inspircd.h" + +using irc::sockets::OpenTCPSocket; + +bool InspSocket::Readable() +{ + return ((this->state != I_CONNECTING) && (this->WaitingForWriteEvent == false)); +} + +InspSocket::InspSocket(InspIRCd* SI) +{ + this->Timeout = NULL; + this->state = I_DISCONNECTED; + this->fd = -1; + this->WaitingForWriteEvent = false; + this->Instance = SI; + this->IsIOHooked = false; +} + +InspSocket::InspSocket(InspIRCd* SI, int newfd, const char* ip) +{ + this->Timeout = NULL; + this->fd = newfd; + this->state = I_CONNECTED; + strlcpy(this->IP,ip,MAXBUF); + this->WaitingForWriteEvent = false; + this->Instance = SI; + this->IsIOHooked = false; + if (this->fd > -1) + this->Instance->SE->AddFd(this); +} + +InspSocket::InspSocket(InspIRCd* SI, const std::string &ipaddr, int aport, bool listening, unsigned long maxtime, const std::string &connectbindip) +{ + this->cbindip = connectbindip; + this->fd = -1; + this->Instance = SI; + strlcpy(host,ipaddr.c_str(),MAXBUF); + this->WaitingForWriteEvent = false; + this->IsIOHooked = false; + this->Timeout = NULL; + if (listening) + { + if ((this->fd = OpenTCPSocket(host)) == ERROR) + { + this->fd = -1; + this->state = I_ERROR; + this->OnError(I_ERR_SOCKET); + return; + } + else + { + if (!SI->BindSocket(this->fd,aport,(char*)ipaddr.c_str())) + { + this->Close(); + this->fd = -1; + this->state = I_ERROR; + this->OnError(I_ERR_BIND); + this->ClosePending = true; + return; + } + else + { + this->state = I_LISTENING; + this->port = aport; + if (this->fd > -1) + { + if (!this->Instance->SE->AddFd(this)) + { + this->Close(); + this->state = I_ERROR; + this->OnError(I_ERR_NOMOREFDS); + } + } + return; + } + } + } + else + { + strlcpy(this->host,ipaddr.c_str(),MAXBUF); + this->port = aport; + + bool ipvalid = true; +#ifdef IPV6 + if (strchr(host,':')) + { + in6_addr n; + if (inet_pton(AF_INET6, host, &n) < 1) + ipvalid = false; + } + else +#endif + { + in_addr n; + if (inet_aton(host,&n) < 1) + ipvalid = false; + } + if (!ipvalid) + { + this->Instance->Log(DEBUG,"BUG: Hostname passed to InspSocket, rather than an IP address!"); + this->OnError(I_ERR_CONNECT); + this->Close(); + this->fd = -1; + this->state = I_ERROR; + return; + } + else + { + strlcpy(this->IP,host,MAXBUF); + timeout_val = maxtime; + if (!this->DoConnect()) + { + this->OnError(I_ERR_CONNECT); + this->Close(); + this->fd = -1; + this->state = I_ERROR; + return; + } + } + } +} + +void InspSocket::WantWrite() +{ + this->Instance->SE->WantWrite(this); + this->WaitingForWriteEvent = true; +} + +void InspSocket::SetQueues(int nfd) +{ + // attempt to increase socket sendq and recvq as high as its possible + int sendbuf = 32768; + int recvbuf = 32768; + setsockopt(nfd,SOL_SOCKET,SO_SNDBUF,(const char *)&sendbuf,sizeof(sendbuf)); + setsockopt(nfd,SOL_SOCKET,SO_RCVBUF,(const char *)&recvbuf,sizeof(sendbuf)); +} + +/* Most irc servers require you to specify the ip you want to bind to. + * If you dont specify an IP, they rather dumbly bind to the first IP + * of the box (e.g. INADDR_ANY). In InspIRCd, we scan thought the IP + * addresses we've bound server ports to, and we try and bind our outbound + * connections to the first usable non-loopback and non-any IP we find. + * This is easier to configure when you have a lot of links and a lot + * of servers to configure. + */ +bool InspSocket::BindAddr(const std::string &ip) +{ + ConfigReader Conf(this->Instance); + socklen_t size = sizeof(sockaddr_in); +#ifdef IPV6 + bool v6 = false; + /* Are we looking for a binding to fit an ipv6 host? */ + if ((ip.empty()) || (ip.find(':') != std::string::npos)) + v6 = true; +#endif + int j = 0; + while (j < Conf.Enumerate("bind") || (!ip.empty())) + { + std::string IP = ip.empty() ? Conf.ReadValue("bind","address",j) : ip; + if (!ip.empty() || Conf.ReadValue("bind","type",j) == "servers") + { + if (!ip.empty() || ((IP != "*") && (IP != "127.0.0.1") && (!IP.empty()) && (IP != "::1"))) + { + sockaddr* s = new sockaddr[2]; +#ifdef IPV6 + if (v6) + { + in6_addr n; + if (inet_pton(AF_INET6, IP.c_str(), &n) > 0) + { + memcpy(&((sockaddr_in6*)s)->sin6_addr, &n, sizeof(n)); + ((sockaddr_in6*)s)->sin6_port = 0; + ((sockaddr_in6*)s)->sin6_family = AF_INET6; + size = sizeof(sockaddr_in6); + } + else + { + delete[] s; + j++; + continue; + } + } + else +#endif + { + in_addr n; + if (inet_aton(IP.c_str(), &n) > 0) + { + ((sockaddr_in*)s)->sin_addr = n; + ((sockaddr_in*)s)->sin_port = 0; + ((sockaddr_in*)s)->sin_family = AF_INET; + } + else + { + delete[] s; + j++; + continue; + } + } + + if (bind(this->fd, s, size) < 0) + { + this->state = I_ERROR; + this->OnError(I_ERR_BIND); + this->fd = -1; + delete[] s; + return false; + } + + delete[] s; + return true; + } + } + j++; + } + return true; +} + +bool InspSocket::DoConnect() +{ + sockaddr* addr = new sockaddr[2]; + socklen_t size = sizeof(sockaddr_in); +#ifdef IPV6 + bool v6 = false; + if ((!*this->host) || strchr(this->host, ':')) + v6 = true; + + if (v6) + { + this->fd = socket(AF_INET6, SOCK_STREAM, 0); + if ((this->fd > -1) && ((strstr(this->IP,"::ffff:") != (char*)&this->IP) && (strstr(this->IP,"::FFFF:") != (char*)&this->IP))) + { + if (!this->BindAddr(this->cbindip)) + { + delete[] addr; + return false; + } + } + } + else +#endif + { + this->fd = socket(AF_INET, SOCK_STREAM, 0); + if (this->fd > -1) + { + if (!this->BindAddr(this->cbindip)) + { + delete[] addr; + return false; + } + } + } + + if (this->fd == -1) + { + this->state = I_ERROR; + this->OnError(I_ERR_SOCKET); + delete[] addr; + return false; + } + +#ifdef IPV6 + if (v6) + { + in6_addr addy; + if (inet_pton(AF_INET6, this->host, &addy) > 0) + { + ((sockaddr_in6*)addr)->sin6_family = AF_INET6; + memcpy(&((sockaddr_in6*)addr)->sin6_addr, &addy, sizeof(addy)); + ((sockaddr_in6*)addr)->sin6_port = htons(this->port); + size = sizeof(sockaddr_in6); + } + } + else +#endif + { + in_addr addy; + if (inet_aton(this->host, &addy) > 0) + { + ((sockaddr_in*)addr)->sin_family = AF_INET; + ((sockaddr_in*)addr)->sin_addr = addy; + ((sockaddr_in*)addr)->sin_port = htons(this->port); + } + } +#ifndef WIN32 + int flags = fcntl(this->fd, F_GETFL, 0); + fcntl(this->fd, F_SETFL, flags | O_NONBLOCK); +#else + unsigned long flags = 0; + ioctlsocket(this->fd, FIONBIO, &flags); +#endif + if (connect(this->fd, (sockaddr*)addr, size) == -1) + { + if (errno != EINPROGRESS) + { + this->OnError(I_ERR_CONNECT); + this->Close(); + this->state = I_ERROR; + return false; + } + + this->Timeout = new SocketTimeout(this->GetFd(), this->Instance, this, timeout_val, this->Instance->Time()); + this->Instance->Timers->AddTimer(this->Timeout); + } + this->state = I_CONNECTING; + if (this->fd > -1) + { + if (!this->Instance->SE->AddFd(this)) + { + this->OnError(I_ERR_NOMOREFDS); + this->Close(); + this->state = I_ERROR; + return false; + } + this->SetQueues(this->fd); + } + return true; +} + + +void InspSocket::Close() +{ + /* Save this, so we dont lose it, + * otherise on failure, error messages + * might be inaccurate. + */ + int save = errno; + if (this->fd > -1) + { + if (this->IsIOHooked && Instance->Config->GetIOHook(this)) + { + try + { + Instance->Config->GetIOHook(this)->OnRawSocketClose(this->fd); + } + catch (CoreException& modexcept) + { + Instance->Log(DEFAULT,"%s threw an exception: %s", modexcept.GetSource(), modexcept.GetReason()); + } + } + this->OnClose(); + shutdown(this->fd,2); + close(this->fd); + + if (Instance->SocketCull.find(this) == Instance->SocketCull.end()) + Instance->SocketCull[this] = this; + } + errno = save; +} + +std::string InspSocket::GetIP() +{ + return this->IP; +} + +char* InspSocket::Read() +{ +#ifdef WINDOWS + if ((fd < 0) || (m_internalFd > MAX_DESCRIPTORS)) +#else + if ((fd < 0) || (fd > MAX_DESCRIPTORS)) +#endif + return NULL; + + int n = 0; + + if (this->IsIOHooked) + { + int result2 = 0; + int MOD_RESULT = 0; + try + { + MOD_RESULT = Instance->Config->GetIOHook(this)->OnRawSocketRead(this->fd,this->ibuf,sizeof(this->ibuf),result2); + } + catch (CoreException& modexcept) + { + Instance->Log(DEFAULT,"%s threw an exception: %s", modexcept.GetSource(), modexcept.GetReason()); + } + if (MOD_RESULT < 0) + { + n = -1; + errno = EAGAIN; + } + else + { + n = result2; + } + } + else + { + n = recv(this->fd,this->ibuf,sizeof(this->ibuf),0); + } + + if ((n > 0) && (n <= (int)sizeof(this->ibuf))) + { + ibuf[n] = 0; + return ibuf; + } + else + { + int err = errno; + if (err == EAGAIN) + return ""; + else + return NULL; + } +} + +void InspSocket::MarkAsClosed() +{ +} + +// There are two possible outcomes to this function. +// It will either write all of the data, or an undefined amount. +// If an undefined amount is written the connection has failed +// and should be aborted. +int InspSocket::Write(const std::string &data) +{ + /* Try and append the data to the back of the queue, and send it on its way + */ + outbuffer.push_back(data); + this->Instance->SE->WantWrite(this); + return (!this->FlushWriteBuffer()); +} + +bool InspSocket::FlushWriteBuffer() +{ + errno = 0; + if ((this->fd > -1) && (this->state == I_CONNECTED)) + { + if (this->IsIOHooked) + { + while (outbuffer.size() && (errno != EAGAIN)) + { + try + { + /* XXX: The lack of buffering here is NOT a bug, modules implementing this interface have to + * implement their own buffering mechanisms + */ + Instance->Config->GetIOHook(this)->OnRawSocketWrite(this->fd, outbuffer[0].c_str(), outbuffer[0].length()); + outbuffer.pop_front(); + } + catch (CoreException& modexcept) + { + Instance->Log(DEBUG,"%s threw an exception: %s", modexcept.GetSource(), modexcept.GetReason()); + return true; + } + } + } + else + { + /* If we have multiple lines, try to send them all, + * not just the first one -- Brain + */ + while (outbuffer.size() && (errno != EAGAIN)) + { + /* Send a line */ +#ifndef WIN32 + int result = write(this->fd,outbuffer[0].c_str(),outbuffer[0].length()); +#else + int result = send(this->fd,outbuffer[0].c_str(),outbuffer[0].length(), 0); +#endif + if (result > 0) + { + if ((unsigned int)result >= outbuffer[0].length()) + { + /* The whole block was written (usually a line) + * Pop the block off the front of the queue, + * dont set errno, because we are clear of errors + * and want to try and write the next block too. + */ + outbuffer.pop_front(); + } + else + { + std::string temp = outbuffer[0].substr(result); + outbuffer[0] = temp; + /* We didnt get the whole line out. arses. + * Try again next time, i guess. Set errno, + * because we shouldnt be writing any more now, + * until the socketengine says its safe to do so. + */ + errno = EAGAIN; + } + } + else if ((result == -1) && (errno != EAGAIN)) + { + this->OnError(I_ERR_WRITE); + this->state = I_ERROR; + this->Instance->SE->DelFd(this); + this->Close(); + return true; + } + } + } + } + + if ((errno == EAGAIN) && (fd > -1)) + { + this->Instance->SE->WantWrite(this); + } + + return (fd < 0); +} + +void SocketTimeout::Tick(time_t now) +{ + if (ServerInstance->SE->GetRef(this->sfd) != this->sock) + return; + + if (this->sock->state == I_CONNECTING) + { + // for non-listening sockets, the timeout can occur + // which causes termination of the connection after + // the given number of seconds without a successful + // connection. + this->sock->OnTimeout(); + this->sock->OnError(I_ERR_TIMEOUT); + this->sock->timeout = true; + + /* NOTE: We must set this AFTER DelFd, as we added + * this socket whilst writeable. This means that we + * must DELETE the socket whilst writeable too! + */ + this->sock->state = I_ERROR; + + if (ServerInstance->SocketCull.find(this->sock) == ServerInstance->SocketCull.end()) + ServerInstance->SocketCull[this->sock] = this->sock; + } + + this->sock->Timeout = NULL; +} + +bool InspSocket::Poll() +{ +#ifdef WINDOWS + if(Instance->SE->GetRef(this->fd) != this) + return false; + int incoming = -1; +#else + if (this->Instance->SE->GetRef(this->fd) != this) + return false; + + int incoming = -1; + + if ((fd < 0) || (fd > MAX_DESCRIPTORS)) + return false; +#endif + switch (this->state) + { + case I_CONNECTING: + /* Our socket was in write-state, so delete it and re-add it + * in read-state. + */ +#ifndef WINDOWS + if (this->fd > -1) + { + this->Instance->SE->DelFd(this); + this->SetState(I_CONNECTED); + if (!this->Instance->SE->AddFd(this)) + return false; + } +#else + this->SetState(I_CONNECTED); +#endif + Instance->Log(DEBUG,"Inspsocket I_CONNECTING state"); + if (Instance->Config->GetIOHook(this)) + { + Instance->Log(DEBUG,"Hook for raw connect"); + try + { + Instance->Config->GetIOHook(this)->OnRawSocketConnect(this->fd); + } + catch (CoreException& modexcept) + { + Instance->Log(DEBUG,"%s threw an exception: %s", modexcept.GetSource(), modexcept.GetReason()); + } + } + return this->OnConnected(); + break; + case I_LISTENING: + { + sockaddr* client = new sockaddr[2]; + length = sizeof (sockaddr_in); + std::string recvip; +#ifdef IPV6 + if ((!*this->host) || strchr(this->host, ':')) + length = sizeof(sockaddr_in6); +#endif + incoming = _accept (this->fd, client, &length); +#ifdef IPV6 + if ((!*this->host) || strchr(this->host, ':')) + { + char buf[1024]; + recvip = inet_ntop(AF_INET6, &((sockaddr_in6*)client)->sin6_addr, buf, sizeof(buf)); + } + else +#endif + recvip = inet_ntoa(((sockaddr_in*)client)->sin_addr); + this->OnIncomingConnection(incoming, (char*)recvip.c_str()); + + if (this->IsIOHooked) + { + try + { + Instance->Config->GetIOHook(this)->OnRawSocketAccept(incoming, recvip.c_str(), this->port); + } + catch (CoreException& modexcept) + { + Instance->Log(DEBUG,"%s threw an exception: %s", modexcept.GetSource(), modexcept.GetReason()); + } + } + + this->SetQueues(incoming); + + delete[] client; + return true; + } + break; + case I_CONNECTED: + /* Process the read event */ + return this->OnDataReady(); + break; + default: + break; + } + return true; +} + +void InspSocket::SetState(InspSocketState s) +{ + this->state = s; +} + +InspSocketState InspSocket::GetState() +{ + return this->state; +} + +int InspSocket::GetFd() +{ + return this->fd; +} + +bool InspSocket::OnConnected() { return true; } +void InspSocket::OnError(InspSocketError e) { return; } +int InspSocket::OnDisconnect() { return 0; } +int InspSocket::OnIncomingConnection(int newfd, char* ip) { return 0; } +bool InspSocket::OnDataReady() { return true; } +bool InspSocket::OnWriteReady() { return true; } +void InspSocket::OnTimeout() { return; } +void InspSocket::OnClose() { return; } + +InspSocket::~InspSocket() +{ + this->Close(); + if (Timeout) + { + Instance->Timers->DelTimer(Timeout); + Timeout = NULL; + } +} + +void InspSocket::HandleEvent(EventType et, int errornum) +{ + switch (et) + { + case EVENT_ERROR: + switch (errornum) + { + case ETIMEDOUT: + this->OnError(I_ERR_TIMEOUT); + break; + case ECONNREFUSED: + case 0: + this->OnError(this->state == I_CONNECTING ? I_ERR_CONNECT : I_ERR_WRITE); + break; + case EADDRINUSE: + this->OnError(I_ERR_BIND); + break; + case EPIPE: + case EIO: + this->OnError(I_ERR_WRITE); + break; + } + if (this->Instance->SocketCull.find(this) == this->Instance->SocketCull.end()) + this->Instance->SocketCull[this] = this; + return; + break; + case EVENT_READ: + if (!this->Poll()) + { + if (this->Instance->SocketCull.find(this) == this->Instance->SocketCull.end()) + this->Instance->SocketCull[this] = this; + return; + } + break; + case EVENT_WRITE: + if (this->WaitingForWriteEvent) + { + this->WaitingForWriteEvent = false; + if (!this->OnWriteReady()) + { + if (this->Instance->SocketCull.find(this) == this->Instance->SocketCull.end()) + this->Instance->SocketCull[this] = this; + return; + } + } + if (this->state == I_CONNECTING) + { + /* This might look wrong as if we should be actually calling + * with EVENT_WRITE, but trust me it is correct. There are some + * writeability-state things in the read code, because of how + * InspSocket used to work regarding write buffering in previous + * versions of InspIRCd. - Brain + */ + this->HandleEvent(EVENT_READ); + return; + } + else + { + if (this->FlushWriteBuffer()) + { + if (this->Instance->SocketCull.find(this) == this->Instance->SocketCull.end()) + this->Instance->SocketCull[this] = this; + return; + } + } + break; + } +} + diff --git a/src/inspstring.cpp b/src/inspstring.cpp index b5278a6ad..98e7228d5 100644 --- a/src/inspstring.cpp +++ b/src/inspstring.cpp @@ -1 +1,137 @@ -/* +------------------------------------+
* | 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 "inspstring.h"
/*
* Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED `AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
* THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef HAS_STRLCPY
CoreExport size_t strlcat(char *dst, const char *src, size_t siz)
{
char *d = dst;
const char *s = src;
size_t n = siz, dlen;
while (n-- != 0 && *d != '\0')
d++;
dlen = d - dst;
n = siz - dlen;
if (n == 0)
return(dlen + strlen(s));
while (*s != '\0')
{
if (n != 1)
{
*d++ = *s;
n--;
}
s++;
}
*d = '\0';
return(dlen + (s - src)); /* count does not include NUL */
}
CoreExport size_t strlcpy(char *dst, const char *src, size_t siz)
{
char *d = dst;
const char *s = src;
size_t n = siz;
/* Copy as many bytes as will fit */
if (n != 0 && --n != 0)
{
do
{
if ((*d++ = *s++) == 0)
break;
} while (--n != 0);
}
/* Not enough room in dst, add NUL and traverse rest of src */
if (n == 0)
{
if (siz != 0)
*d = '\0'; /* NUL-terminate dst */
while (*s++);
}
return(s - src - 1); /* count does not include NUL */
}
#endif
CoreExport int charlcat(char* x,char y,int z)
{
char* x__n = x;
int v = 0;
while(*x__n++)
v++;
if (v < z - 1)
{
*--x__n = y;
*++x__n = 0;
}
return v;
}
CoreExport bool charremove(char* mp, char remove)
{
char* mptr = mp;
bool shift_down = false;
while (*mptr)
{
if (*mptr == remove)
shift_down = true;
if (shift_down)
*mptr = *(mptr+1);
mptr++;
}
return shift_down;
}
\ No newline at end of file +/* +------------------------------------+ + * | 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 "inspstring.h" + +/* + * Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED `AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef HAS_STRLCPY +CoreExport size_t strlcat(char *dst, const char *src, size_t siz) +{ + char *d = dst; + const char *s = src; + size_t n = siz, dlen; + + while (n-- != 0 && *d != '\0') + d++; + + dlen = d - dst; + n = siz - dlen; + + if (n == 0) + return(dlen + strlen(s)); + + while (*s != '\0') + { + if (n != 1) + { + *d++ = *s; + n--; + } + + s++; + } + + *d = '\0'; + return(dlen + (s - src)); /* count does not include NUL */ +} + +CoreExport size_t strlcpy(char *dst, const char *src, size_t siz) +{ + char *d = dst; + const char *s = src; + size_t n = siz; + + /* Copy as many bytes as will fit */ + if (n != 0 && --n != 0) + { + do + { + if ((*d++ = *s++) == 0) + break; + } while (--n != 0); + } + + /* Not enough room in dst, add NUL and traverse rest of src */ + if (n == 0) + { + if (siz != 0) + *d = '\0'; /* NUL-terminate dst */ + while (*s++); + } + + return(s - src - 1); /* count does not include NUL */ +} +#endif + +CoreExport int charlcat(char* x,char y,int z) +{ + char* x__n = x; + int v = 0; + + while(*x__n++) + v++; + + if (v < z - 1) + { + *--x__n = y; + *++x__n = 0; + } + + return v; +} + +CoreExport bool charremove(char* mp, char remove) +{ + char* mptr = mp; + bool shift_down = false; + + while (*mptr) + { + if (*mptr == remove) + shift_down = true; + + if (shift_down) + *mptr = *(mptr+1); + + mptr++; + } + + return shift_down; +} + diff --git a/src/mode.cpp b/src/mode.cpp index cfb009273..db120cfe5 100644 --- a/src/mode.cpp +++ b/src/mode.cpp @@ -1 +1,1066 @@ -/* +------------------------------------+
* | 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 "users.h"
#include "modules.h"
#include "inspstring.h"
#include "mode.h"
/* +s (secret) */
#include "modes/cmode_s.h"
/* +p (private) */
#include "modes/cmode_p.h"
/* +b (bans) */
#include "modes/cmode_b.h"
/* +m (moderated) */
#include "modes/cmode_m.h"
/* +t (only (half) ops can change topic) */
#include "modes/cmode_t.h"
/* +n (no external messages) */
#include "modes/cmode_n.h"
/* +i (invite only) */
#include "modes/cmode_i.h"
/* +k (keyed channel) */
#include "modes/cmode_k.h"
/* +l (channel user limit) */
#include "modes/cmode_l.h"
/* +o (channel op) */
#include "modes/cmode_o.h"
/* +h (channel halfop) */
#include "modes/cmode_h.h"
/* +v (channel voice) */
#include "modes/cmode_v.h"
/* +s (server notices) */
#include "modes/umode_s.h"
/* +w (see wallops) */
#include "modes/umode_w.h"
/* +i (invisible) */
#include "modes/umode_i.h"
/* +o (operator) */
#include "modes/umode_o.h"
/* +n (notice mask - our implementation of snomasks) */
#include "modes/umode_n.h"
ModeHandler::ModeHandler(InspIRCd* Instance, char modeletter, int parameters_on, int parameters_off, bool listmode, ModeType type, bool operonly, char mprefix)
: ServerInstance(Instance), mode(modeletter), n_params_on(parameters_on), n_params_off(parameters_off), list(listmode), m_type(type), oper(operonly), prefix(mprefix), count(0)
{
}
ModeHandler::~ModeHandler()
{
}
bool ModeHandler::IsListMode()
{
return list;
}
unsigned int ModeHandler::GetPrefixRank()
{
return 0;
}
unsigned int ModeHandler::GetCount()
{
return 0;
}
void ModeHandler::ChangeCount(int modifier)
{
count += modifier;
}
ModeType ModeHandler::GetModeType()
{
return m_type;
}
bool ModeHandler::NeedsOper()
{
return oper;
}
char ModeHandler::GetPrefix()
{
return prefix;
}
int ModeHandler::GetNumParams(bool adding)
{
return adding ? n_params_on : n_params_off;
}
char ModeHandler::GetModeChar()
{
return mode;
}
ModeAction ModeHandler::OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string ¶meter, bool adding)
{
return MODEACTION_DENY;
}
ModePair ModeHandler::ModeSet(userrec* source, userrec* dest, chanrec* channel, const std::string ¶meter)
{
if (dest)
{
return std::make_pair(dest->IsModeSet(this->mode), "");
}
else
{
return std::make_pair(channel->IsModeSet(this->mode), "");
}
}
void ModeHandler::DisplayList(userrec* user, chanrec* channel)
{
}
bool ModeHandler::CheckTimeStamp(time_t theirs, time_t ours, const std::string &their_param, const std::string &our_param, chanrec* channel)
{
return (ours < theirs);
}
ModeWatcher::ModeWatcher(InspIRCd* Instance, char modeletter, ModeType type) : ServerInstance(Instance), mode(modeletter), m_type(type)
{
}
ModeWatcher::~ModeWatcher()
{
}
char ModeWatcher::GetModeChar()
{
return mode;
}
ModeType ModeWatcher::GetModeType()
{
return m_type;
}
bool ModeWatcher::BeforeMode(userrec* source, userrec* dest, chanrec* channel, std::string ¶meter, bool adding, ModeType type)
{
return true;
}
void ModeWatcher::AfterMode(userrec* source, userrec* dest, chanrec* channel, const std::string ¶meter, bool adding, ModeType type)
{
}
userrec* ModeParser::SanityChecks(userrec *user,const char *dest,chanrec *chan,int status)
{
userrec *d;
if ((!user) || (!dest) || (!chan) || (!*dest))
{
return NULL;
}
d = ServerInstance->FindNick(dest);
if (!d)
{
user->WriteServ("401 %s %s :No such nick/channel",user->nick, dest);
return NULL;
}
return d;
}
const char* ModeParser::Grant(userrec *d,chanrec *chan,int MASK)
{
if (!chan)
return "";
UCListIter n = d->chans.find(chan);
if (n != d->chans.end())
{
if (n->second & MASK)
{
return "";
}
n->second = n->second | MASK;
switch (MASK)
{
case UCMODE_OP:
n->first->AddOppedUser(d);
break;
case UCMODE_HOP:
n->first->AddHalfoppedUser(d);
break;
case UCMODE_VOICE:
n->first->AddVoicedUser(d);
break;
}
return d->nick;
}
return "";
}
const char* ModeParser::Revoke(userrec *d,chanrec *chan,int MASK)
{
if (!chan)
return "";
UCListIter n = d->chans.find(chan);
if (n != d->chans.end())
{
if ((n->second & MASK) == 0)
{
return "";
}
n->second ^= MASK;
switch (MASK)
{
case UCMODE_OP:
n->first->DelOppedUser(d);
break;
case UCMODE_HOP:
n->first->DelHalfoppedUser(d);
break;
case UCMODE_VOICE:
n->first->DelVoicedUser(d);
break;
}
return d->nick;
}
return "";
}
void ModeParser::DisplayCurrentModes(userrec *user, userrec* targetuser, chanrec* targetchannel, const char* text)
{
if (targetchannel)
{
/* Display channel's current mode string */
user->WriteServ("324 %s %s +%s",user->nick, targetchannel->name, targetchannel->ChanModes(targetchannel->HasUser(user)));
user->WriteServ("329 %s %s %lu", user->nick, targetchannel->name, (unsigned long)targetchannel->age);
return;
}
else if (targetuser)
{
if (targetuser->Visibility && !targetuser->Visibility->VisibleTo(user))
{
user->WriteServ("401 %s %s :No such nick/channel",user->nick, text);
return;
}
if ((targetuser == user) || (IS_OPER(user)))
{
/* Display user's current mode string */
user->WriteServ("221 %s :+%s",targetuser->nick,targetuser->FormatModes());
if (IS_OPER(targetuser))
user->WriteServ("008 %s +%s :Server notice mask", targetuser->nick, targetuser->FormatNoticeMasks());
return;
}
else
{
user->WriteServ("502 %s :Can't change mode for other users", user->nick);
return;
}
}
/* No such nick/channel */
user->WriteServ("401 %s %s :No such nick/channel",user->nick, text);
return;
}
void ModeParser::Process(const char** parameters, int pcnt, userrec *user, bool servermode)
{
std::string target = parameters[0];
ModeType type = MODETYPE_USER;
unsigned char mask = 0;
chanrec* targetchannel = ServerInstance->FindChan(parameters[0]);
userrec* targetuser = ServerInstance->FindNick(parameters[0]);
LastParse.clear();
/* Special case for displaying the list for listmodes,
* e.g. MODE #chan b, or MODE #chan +b without a parameter
*/
if ((targetchannel) && (pcnt == 2))
{
const char* mode = parameters[1];
int nonlistmodes_found = 0;
bool sent[256];
mask = MASK_CHANNEL;
memset(&sent, 0, 256);
while (mode && *mode)
{
unsigned char mletter = *mode;
if (*mode == '+')
{
mode++;
continue;
}
/* Ensure the user doesnt request the same mode twice,
* so they cant flood themselves off out of idiocy.
*/
if (!sent[mletter])
{
sent[mletter] = true;
}
else
{
mode++;
continue;
}
ModeHandler *mh = this->FindMode(*mode, MODETYPE_CHANNEL);
bool display = true;
if ((mh) && (mh->IsListMode()))
{
if (ServerInstance->Config->HideModeLists[mletter] && (targetchannel->GetStatus(user) < STATUS_HOP))
{
user->WriteServ("482 %s %s :Only half-operators and above may view the +%c list",user->nick, targetchannel->name, *mode++);
continue;
}
/** See below for a description of what craq this is :D
*/
unsigned char handler_id = (*mode - 65) | mask;
for(ModeWatchIter watchers = modewatchers[handler_id].begin(); watchers != modewatchers[handler_id].end(); watchers++)
{
std::string dummyparam;
if (!((*watchers)->BeforeMode(user, NULL, targetchannel, dummyparam, true, MODETYPE_CHANNEL)))
display = false;
}
if (display)
mh->DisplayList(user, targetchannel);
}
else
nonlistmodes_found++;
mode++;
}
/* We didnt have any modes that were non-list, we can return here */
if (!nonlistmodes_found)
return;
}
if (pcnt == 1)
{
this->DisplayCurrentModes(user, targetuser, targetchannel, parameters[0]);
}
else if (pcnt > 1)
{
if (targetchannel)
{
type = MODETYPE_CHANNEL;
mask = MASK_CHANNEL;
/* Extra security checks on channel modes
* (e.g. are they a (half)op?
*/
if ((IS_LOCAL(user)) && (targetchannel->GetStatus(user) < STATUS_HOP))
{
/* We don't have halfop */
int MOD_RESULT = 0;
FOREACH_RESULT(I_OnAccessCheck,OnAccessCheck(user, NULL, targetchannel, AC_GENERAL_MODE));
if (MOD_RESULT == ACR_DENY)
return;
if (MOD_RESULT == ACR_DEFAULT)
{
/* Are we a uline or is it a servermode? */
if ((!ServerInstance->ULine(user->server)) && (!servermode))
{
/* Not enough permission:
* NOT a uline and NOT a servermode,
* OR, NOT halfop or above.
*/
user->WriteServ("482 %s %s :You're not a channel (half)operator",user->nick, targetchannel->name);
return;
}
}
}
}
else if (targetuser)
{
type = MODETYPE_USER;
mask = MASK_USER;
if ((user != targetuser) && (!ServerInstance->ULine(user->server)))
{
user->WriteServ("502 %s :Can't change mode for other users", user->nick);
return;
}
}
else
{
/* No such nick/channel */
user->WriteServ("401 %s %s :No such nick/channel",user->nick, parameters[0]);
return;
}
std::string mode_sequence = parameters[1];
std::string parameter;
std::ostringstream parameter_list;
std::string output_sequence;
bool adding = true, state_change = false;
unsigned char handler_id = 0;
int parameter_counter = 2; /* Index of first parameter */
int parameter_count = 0;
bool last_successful_state_change = false;
/* A mode sequence that doesnt start with + or -. Assume +. - Thanks for the suggestion spike (bug#132) */
if ((*mode_sequence.begin() != '+') && (*mode_sequence.begin() != '-'))
mode_sequence.insert(0, "+");
for (std::string::const_iterator letter = mode_sequence.begin(); letter != mode_sequence.end(); letter++)
{
unsigned char modechar = *letter;
switch (modechar)
{
/* NB:
* For + and - mode characters, we don't just stick the character into the output sequence.
* This is because the user may do something dumb, like: +-+ooo or +oo-+. To prevent this
* appearing in the output sequence, we store a flag which says there was a state change,
* which is set on any + or -, however, the + or - that we finish on is only appended to
* the output stream in the event it is followed by a non "+ or -" character, such as o or v.
*/
case '+':
/* The following expression prevents: +o+o nick nick, compressing it to +oo nick nick,
* however, will allow the + if it is the first item in the sequence, regardless.
*/
if ((!adding) || (!output_sequence.length()))
state_change = true;
adding = true;
if (!output_sequence.length())
last_successful_state_change = false;
continue;
break;
case '-':
if ((adding) || (!output_sequence.length()))
state_change = true;
adding = false;
if (!output_sequence.length())
last_successful_state_change = true;
continue;
break;
default:
/**
* Watch carefully for the sleight of hand trick.
* 65 is the ascii value of 'A'. We take this from
* the char we're looking at to get a number between
* 1 and 127. We then logic-or it to get the hashed
* position, dependent on wether its a channel or
* a user mode. This is a little stranger, but a lot
* faster, than using a map of pairs.
*/
handler_id = (modechar - 65) | mask;
if (modehandlers[handler_id])
{
bool abort = false;
if (modehandlers[handler_id]->GetModeType() == type)
{
if (modehandlers[handler_id]->GetNumParams(adding))
{
/* This mode expects a parameter, do we have any parameters left in our list to use? */
if (parameter_counter < pcnt)
{
parameter = parameters[parameter_counter++];
/* Yerk, invalid! */
if ((parameter.find(':') == 0) || (parameter.rfind(' ') != std::string::npos))
parameter.clear();
}
else
{
/* No parameter, continue to the next mode */
continue;
}
bool had_parameter = !parameter.empty();
for (ModeWatchIter watchers = modewatchers[handler_id].begin(); watchers != modewatchers[handler_id].end(); watchers++)
{
if ((*watchers)->BeforeMode(user, targetuser, targetchannel, parameter, adding, type) == false)
{
abort = true;
break;
}
/* A module whacked the parameter completely, and there was one. abort. */
if ((had_parameter) && (parameter.empty()))
{
abort = true;
break;
}
}
if (abort)
continue;
}
else
{
/* Fix by brain: mode watchers not being called for parameterless modes */
for (ModeWatchIter watchers = modewatchers[handler_id].begin(); watchers != modewatchers[handler_id].end(); watchers++)
{
if ((*watchers)->BeforeMode(user, targetuser, targetchannel, parameter, adding, type) == false)
{
abort = true;
break;
}
}
if (abort)
continue;
}
/* It's an oper only mode, check if theyre an oper. If they arent,
* eat any parameter that came with the mode, and continue to next
*/
if ((IS_LOCAL(user)) && (modehandlers[handler_id]->NeedsOper()) && (!IS_OPER(user)))
{
user->WriteServ("481 %s :Permission Denied - Only IRC operators may %sset %s mode %c", user->nick,
adding ? "" : "un", type == MODETYPE_CHANNEL ? "channel" : "user",
modehandlers[handler_id]->GetModeChar());
continue;
}
/* Call the handler for the mode */
ModeAction ma = modehandlers[handler_id]->OnModeChange(user, targetuser, targetchannel, parameter, adding);
if ((modehandlers[handler_id]->GetNumParams(adding)) && (parameter.empty()))
{
/* The handler nuked the parameter and they are supposed to have one.
* We CANT continue now, even if they actually returned MODEACTION_ALLOW,
* so we bail to the next mode character.
*/
continue;
}
if (ma == MODEACTION_ALLOW)
{
/* We're about to output a valid mode letter - was there previously a pending state-change? */
if (state_change)
{
if (adding != last_successful_state_change)
output_sequence.append(adding ? "+" : "-");
last_successful_state_change = adding;
}
/* Add the mode letter */
output_sequence.push_back(modechar);
/* Is there a valid parameter for this mode? If so add it to the parameter list */
if ((modehandlers[handler_id]->GetNumParams(adding)) && (!parameter.empty()))
{
parameter_list << " " << parameter;
parameter_count++;
/* Does this mode have a prefix? */
if (modehandlers[handler_id]->GetPrefix() && targetchannel)
{
userrec* user_to_prefix = ServerInstance->FindNick(parameter);
if (user_to_prefix)
targetchannel->SetPrefix(user_to_prefix, modehandlers[handler_id]->GetPrefix(),
modehandlers[handler_id]->GetPrefixRank(), adding);
}
}
/* Call all the AfterMode events in the mode watchers for this mode */
for (ModeWatchIter watchers = modewatchers[handler_id].begin(); watchers != modewatchers[handler_id].end(); watchers++)
(*watchers)->AfterMode(user, targetuser, targetchannel, parameter, adding, type);
/* Reset the state change flag */
state_change = false;
if ((output_sequence.length() + parameter_list.str().length() > 450) || (output_sequence.length() > 100)
|| (parameter_count > MAXMODES))
{
/* We cant have a mode sequence this long */
letter = mode_sequence.end() - 1;
continue;
}
}
}
}
else
{
/* No mode handler? Unknown mode character then. */
user->WriteServ("472 %s %c :is unknown mode char to me",user->nick, modechar);
}
break;
}
}
/* Was there at least one valid mode in the sequence? */
if (!output_sequence.empty())
{
if (servermode)
{
if (type == MODETYPE_CHANNEL)
{
targetchannel->WriteChannelWithServ(ServerInstance->Config->ServerName, "MODE %s %s%s", targetchannel->name, output_sequence.c_str(), parameter_list.str().c_str());
this->LastParse = targetchannel->name;
}
else
{
targetuser->WriteServ("MODE %s %s%s",targetuser->nick,output_sequence.c_str(), parameter_list.str().c_str());
this->LastParse = targetuser->nick;
}
}
else
{
if (type == MODETYPE_CHANNEL)
{
targetchannel->WriteChannel(user,"MODE %s %s%s",targetchannel->name,output_sequence.c_str(),parameter_list.str().c_str());
FOREACH_MOD(I_OnMode,OnMode(user, targetchannel, TYPE_CHANNEL, output_sequence + parameter_list.str()));
this->LastParse = targetchannel->name;
}
else
{
user->WriteTo(targetuser,"MODE %s %s%s",targetuser->nick,output_sequence.c_str(), parameter_list.str().c_str());
FOREACH_MOD(I_OnMode,OnMode(user, targetuser, TYPE_USER, output_sequence + parameter_list.str()));
this->LastParse = targetuser->nick;
}
}
LastParse.append(" ");
LastParse.append(output_sequence);
LastParse.append(parameter_list.str());
}
}
}
const std::string& ModeParser::GetLastParse()
{
return LastParse;
}
void ModeParser::CleanMask(std::string &mask)
{
std::string::size_type pos_of_pling = mask.find_first_of('!');
std::string::size_type pos_of_at = mask.find_first_of('@');
std::string::size_type pos_of_dot = mask.find_first_of('.');
std::string::size_type pos_of_colon = mask.find_first_of(':'); /* Because ipv6 addresses are colon delimited */
if ((pos_of_pling == std::string::npos) && (pos_of_at == std::string::npos))
{
/* Just a nick, or just a host */
if ((pos_of_dot == std::string::npos) && (pos_of_colon == std::string::npos))
{
/* It has no '.' in it, it must be a nick. */
mask.append("!*@*");
}
else
{
/* Got a dot in it? Has to be a host */
mask = "*!*@" + mask;
}
}
else if ((pos_of_pling == std::string::npos) && (pos_of_at != std::string::npos))
{
/* Has an @ but no !, its a user@host */
mask = "*!" + mask;
}
else if ((pos_of_pling != std::string::npos) && (pos_of_at == std::string::npos))
{
/* Has a ! but no @, it must be a nick!ident */
mask.append("@*");
}
}
bool ModeParser::AddMode(ModeHandler* mh, unsigned const char modeletter)
{
unsigned char mask = 0;
unsigned char pos = 0;
/* Yes, i know, this might let people declare modes like '_' or '^'.
* If they do that, thats their problem, and if i ever EVER see an
* official InspIRCd developer do that, i'll beat them with a paddle!
*/
if ((mh->GetModeChar() < 'A') || (mh->GetModeChar() > 'z') || (mh->GetPrefix() > 126))
return false;
/* A mode prefix of ',' is not acceptable, it would fuck up server to server.
* A mode prefix of ':' will fuck up both server to server, and client to server.
* A mode prefix of '#' will mess up /whois and /privmsg
*/
if ((mh->GetPrefix() == ',') || (mh->GetPrefix() == ':') || (mh->GetPrefix() == '#'))
return false;
mh->GetModeType() == MODETYPE_USER ? mask = MASK_USER : mask = MASK_CHANNEL;
pos = (mh->GetModeChar()-65) | mask;
if (modehandlers[pos])
return false;
modehandlers[pos] = mh;
return true;
}
bool ModeParser::DelMode(ModeHandler* mh)
{
unsigned char mask = 0;
unsigned char pos = 0;
if ((mh->GetModeChar() < 'A') || (mh->GetModeChar() > 'z'))
return false;
mh->GetModeType() == MODETYPE_USER ? mask = MASK_USER : mask = MASK_CHANNEL;
pos = (mh->GetModeChar()-65) | mask;
if (!modehandlers[pos])
return false;
switch (mh->GetModeType())
{
case MODETYPE_USER:
for (user_hash::iterator i = ServerInstance->clientlist->begin(); i != ServerInstance->clientlist->end(); i++)
{
mh->RemoveMode(i->second);
}
break;
case MODETYPE_CHANNEL:
for (chan_hash::iterator i = ServerInstance->chanlist->begin(); i != ServerInstance->chanlist->end(); i++)
{
mh->RemoveMode(i->second);
}
break;
}
modehandlers[pos] = NULL;
return true;
}
ModeHandler* ModeParser::FindMode(unsigned const char modeletter, ModeType mt)
{
unsigned char mask = 0;
unsigned char pos = 0;
if ((modeletter < 'A') || (modeletter > 'z'))
return NULL;
mt == MODETYPE_USER ? mask = MASK_USER : mask = MASK_CHANNEL;
pos = (modeletter-65) | mask;
return modehandlers[pos];
}
std::string ModeParser::UserModeList()
{
char modestr[256];
int pointer = 0;
for (unsigned char mode = 'A'; mode <= 'z'; mode++)
{
unsigned char pos = (mode-65) | MASK_USER;
if (modehandlers[pos])
modestr[pointer++] = mode;
}
modestr[pointer++] = 0;
return modestr;
}
std::string ModeParser::ChannelModeList()
{
char modestr[256];
int pointer = 0;
for (unsigned char mode = 'A'; mode <= 'z'; mode++)
{
if ((!ServerInstance->Config->AllowHalfop) && (mode == 'h'))
continue;
unsigned char pos = (mode-65) | MASK_CHANNEL;
if (modehandlers[pos])
modestr[pointer++] = mode;
}
modestr[pointer++] = 0;
return modestr;
}
std::string ModeParser::ParaModeList()
{
char modestr[256];
int pointer = 0;
for (unsigned char mode = 'A'; mode <= 'z'; mode++)
{
if ((!ServerInstance->Config->AllowHalfop) && (mode == 'h'))
continue;
unsigned char pos = (mode-65) | MASK_CHANNEL;
if ((modehandlers[pos]) && (modehandlers[pos]->GetNumParams(true)))
modestr[pointer++] = mode;
}
modestr[pointer++] = 0;
return modestr;
}
ModeHandler* ModeParser::FindPrefix(unsigned const char pfxletter)
{
for (unsigned char mode = 'A'; mode <= 'z'; mode++)
{
unsigned char pos = (mode-65) | MASK_CHANNEL;
if ((modehandlers[pos]) && (modehandlers[pos]->GetPrefix() == pfxletter))
{
return modehandlers[pos];
}
}
return NULL;
}
std::string ModeParser::ModeString(userrec* user, chanrec* channel)
{
std::string types;
std::string pars;
if (!channel || !user)
return "";
for (unsigned char mode = 'A'; mode <= 'z'; mode++)
{
unsigned char pos = (mode-65) | MASK_CHANNEL;
ModeHandler* mh = modehandlers[pos];
if ((mh) && (mh->GetNumParams(true)) && (mh->GetNumParams(false)))
{
ModePair ret;
ret = mh->ModeSet(NULL, user, channel, user->nick);
if ((ret.first) && (ret.second == user->nick))
{
pars.append(" ");
pars.append(user->nick);
types.push_back(mh->GetModeChar());
}
}
}
return types+pars;
}
std::string ModeParser::ChanModes()
{
std::string type1; /* Listmodes EXCEPT those with a prefix */
std::string type2; /* Modes that take a param when adding or removing */
std::string type3; /* Modes that only take a param when adding */
std::string type4; /* Modes that dont take a param */
for (unsigned char mode = 'A'; mode <= 'z'; mode++)
{
if ((!ServerInstance->Config->AllowHalfop) && (mode == 'h'))
continue;
unsigned char pos = (mode-65) | MASK_CHANNEL;
/* One parameter when adding */
if (modehandlers[pos])
{
if (modehandlers[pos]->GetNumParams(true))
{
if ((modehandlers[pos]->IsListMode()) && (!modehandlers[pos]->GetPrefix()))
{
type1 += modehandlers[pos]->GetModeChar();
}
else
{
/* ... and one parameter when removing */
if (modehandlers[pos]->GetNumParams(false))
{
/* But not a list mode */
if (!modehandlers[pos]->GetPrefix())
{
type2 += modehandlers[pos]->GetModeChar();
}
}
else
{
/* No parameters when removing */
type3 += modehandlers[pos]->GetModeChar();
}
}
}
else
{
type4 += modehandlers[pos]->GetModeChar();
}
}
}
return type1 + "," + type2 + "," + type3 + "," + type4;
}
bool ModeParser::PrefixComparison(prefixtype one, prefixtype two)
{
return one.second > two.second;
}
std::string ModeParser::BuildPrefixes()
{
std::string mletters;
std::string mprefixes;
pfxcontainer pfx;
std::map<char,char> prefix_to_mode;
for (unsigned char mode = 'A'; mode <= 'z'; mode++)
{
if ((!ServerInstance->Config->AllowHalfop) && (mode == 'h'))
continue;
unsigned char pos = (mode-65) | MASK_CHANNEL;
if ((modehandlers[pos]) && (modehandlers[pos]->GetPrefix()))
{
pfx.push_back(std::make_pair<char,unsigned int>(modehandlers[pos]->GetPrefix(), modehandlers[pos]->GetPrefixRank()));
prefix_to_mode[modehandlers[pos]->GetPrefix()] = modehandlers[pos]->GetModeChar();
}
}
sort(pfx.begin(), pfx.end(), ModeParser::PrefixComparison);
for (pfxcontainer::iterator n = pfx.begin(); n != pfx.end(); n++)
{
mletters = mletters + n->first;
mprefixes = mprefixes + prefix_to_mode.find(n->first)->second;
}
return "(" + mprefixes + ")" + mletters;
}
bool ModeParser::AddModeWatcher(ModeWatcher* mw)
{
unsigned char mask = 0;
unsigned char pos = 0;
if (!mw)
return false;
if ((mw->GetModeChar() < 'A') || (mw->GetModeChar() > 'z'))
return false;
mw->GetModeType() == MODETYPE_USER ? mask = MASK_USER : mask = MASK_CHANNEL;
pos = (mw->GetModeChar()-65) | mask;
modewatchers[pos].push_back(mw);
return true;
}
bool ModeParser::DelModeWatcher(ModeWatcher* mw)
{
unsigned char mask = 0;
unsigned char pos = 0;
if (!mw)
return false;
if ((mw->GetModeChar() < 'A') || (mw->GetModeChar() > 'z'))
return false;
mw->GetModeType() == MODETYPE_USER ? mask = MASK_USER : mask = MASK_CHANNEL;
pos = (mw->GetModeChar()-65) | mask;
ModeWatchIter a = find(modewatchers[pos].begin(),modewatchers[pos].end(),mw);
if (a == modewatchers[pos].end())
{
return false;
}
modewatchers[pos].erase(a);
return true;
}
/** This default implementation can remove simple user modes
*/
void ModeHandler::RemoveMode(userrec* user)
{
char moderemove[MAXBUF];
const char* parameters[] = { user->nick, moderemove };
if (user->IsModeSet(this->GetModeChar()))
{
sprintf(moderemove,"-%c",this->GetModeChar());
ServerInstance->Parser->CallHandler("MODE", parameters, 2, user);
}
}
/** This default implementation can remove simple channel modes
* (no parameters)
*/
void ModeHandler::RemoveMode(chanrec* channel)
{
char moderemove[MAXBUF];
const char* parameters[] = { channel->name, moderemove };
if (channel->IsModeSet(this->GetModeChar()))
{
userrec* n = new userrec(ServerInstance);
sprintf(moderemove,"-%c",this->GetModeChar());
n->SetFd(FD_MAGIC_NUMBER);
ServerInstance->SendMode(parameters, 2, n);
delete n;
}
}
ModeParser::ModeParser(InspIRCd* Instance) : ServerInstance(Instance)
{
struct Initializer
{
char modechar;
ModeHandler* handler;
};
Initializer modes[] = {
{ 's', new ModeChannelSecret(Instance) },
{ 'p', new ModeChannelPrivate(Instance) },
{ 'm', new ModeChannelModerated(Instance) },
{ 't', new ModeChannelTopicOps(Instance) },
{ 'n', new ModeChannelNoExternal(Instance) },
{ 'i', new ModeChannelInviteOnly(Instance) },
{ 'k', new ModeChannelKey(Instance) },
{ 'l', new ModeChannelLimit(Instance) },
{ 'b', new ModeChannelBan(Instance) },
{ 'o', new ModeChannelOp(Instance) },
{ 'h', new ModeChannelHalfOp(Instance) },
{ 'v', new ModeChannelVoice(Instance) },
{ 's', new ModeUserServerNotice(Instance) },
{ 'w', new ModeUserWallops(Instance) },
{ 'i', new ModeUserInvisible(Instance) },
{ 'o', new ModeUserOperator(Instance) },
{ 'n', new ModeUserServerNoticeMask(Instance) },
{ 0, NULL }
};
/* Clear mode list */
memset(modehandlers, 0, sizeof(modehandlers));
memset(modewatchers, 0, sizeof(modewatchers));
/* Last parse string */
LastParse.clear();
/* Initialise the RFC mode letters */
for (int index = 0; modes[index].modechar; index++)
this->AddMode(modes[index].handler, modes[index].modechar);
}
\ No newline at end of file +/* +------------------------------------+ + * | 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 "users.h" +#include "modules.h" +#include "inspstring.h" +#include "mode.h" + +/* +s (secret) */ +#include "modes/cmode_s.h" +/* +p (private) */ +#include "modes/cmode_p.h" +/* +b (bans) */ +#include "modes/cmode_b.h" +/* +m (moderated) */ +#include "modes/cmode_m.h" +/* +t (only (half) ops can change topic) */ +#include "modes/cmode_t.h" +/* +n (no external messages) */ +#include "modes/cmode_n.h" +/* +i (invite only) */ +#include "modes/cmode_i.h" +/* +k (keyed channel) */ +#include "modes/cmode_k.h" +/* +l (channel user limit) */ +#include "modes/cmode_l.h" +/* +o (channel op) */ +#include "modes/cmode_o.h" +/* +h (channel halfop) */ +#include "modes/cmode_h.h" +/* +v (channel voice) */ +#include "modes/cmode_v.h" +/* +s (server notices) */ +#include "modes/umode_s.h" +/* +w (see wallops) */ +#include "modes/umode_w.h" +/* +i (invisible) */ +#include "modes/umode_i.h" +/* +o (operator) */ +#include "modes/umode_o.h" +/* +n (notice mask - our implementation of snomasks) */ +#include "modes/umode_n.h" + +ModeHandler::ModeHandler(InspIRCd* Instance, char modeletter, int parameters_on, int parameters_off, bool listmode, ModeType type, bool operonly, char mprefix) + : ServerInstance(Instance), mode(modeletter), n_params_on(parameters_on), n_params_off(parameters_off), list(listmode), m_type(type), oper(operonly), prefix(mprefix), count(0) +{ +} + +ModeHandler::~ModeHandler() +{ +} + +bool ModeHandler::IsListMode() +{ + return list; +} + +unsigned int ModeHandler::GetPrefixRank() +{ + return 0; +} + +unsigned int ModeHandler::GetCount() +{ + return 0; +} + +void ModeHandler::ChangeCount(int modifier) +{ + count += modifier; +} + +ModeType ModeHandler::GetModeType() +{ + return m_type; +} + +bool ModeHandler::NeedsOper() +{ + return oper; +} + +char ModeHandler::GetPrefix() +{ + return prefix; +} + +int ModeHandler::GetNumParams(bool adding) +{ + return adding ? n_params_on : n_params_off; +} + +char ModeHandler::GetModeChar() +{ + return mode; +} + +ModeAction ModeHandler::OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string ¶meter, bool adding) +{ + return MODEACTION_DENY; +} + +ModePair ModeHandler::ModeSet(userrec* source, userrec* dest, chanrec* channel, const std::string ¶meter) +{ + if (dest) + { + return std::make_pair(dest->IsModeSet(this->mode), ""); + } + else + { + return std::make_pair(channel->IsModeSet(this->mode), ""); + } +} + +void ModeHandler::DisplayList(userrec* user, chanrec* channel) +{ +} + +bool ModeHandler::CheckTimeStamp(time_t theirs, time_t ours, const std::string &their_param, const std::string &our_param, chanrec* channel) +{ + return (ours < theirs); +} + +ModeWatcher::ModeWatcher(InspIRCd* Instance, char modeletter, ModeType type) : ServerInstance(Instance), mode(modeletter), m_type(type) +{ +} + +ModeWatcher::~ModeWatcher() +{ +} + +char ModeWatcher::GetModeChar() +{ + return mode; +} + +ModeType ModeWatcher::GetModeType() +{ + return m_type; +} + +bool ModeWatcher::BeforeMode(userrec* source, userrec* dest, chanrec* channel, std::string ¶meter, bool adding, ModeType type) +{ + return true; +} + +void ModeWatcher::AfterMode(userrec* source, userrec* dest, chanrec* channel, const std::string ¶meter, bool adding, ModeType type) +{ +} + +userrec* ModeParser::SanityChecks(userrec *user,const char *dest,chanrec *chan,int status) +{ + userrec *d; + if ((!user) || (!dest) || (!chan) || (!*dest)) + { + return NULL; + } + d = ServerInstance->FindNick(dest); + if (!d) + { + user->WriteServ("401 %s %s :No such nick/channel",user->nick, dest); + return NULL; + } + return d; +} + +const char* ModeParser::Grant(userrec *d,chanrec *chan,int MASK) +{ + if (!chan) + return ""; + + UCListIter n = d->chans.find(chan); + if (n != d->chans.end()) + { + if (n->second & MASK) + { + return ""; + } + n->second = n->second | MASK; + switch (MASK) + { + case UCMODE_OP: + n->first->AddOppedUser(d); + break; + case UCMODE_HOP: + n->first->AddHalfoppedUser(d); + break; + case UCMODE_VOICE: + n->first->AddVoicedUser(d); + break; + } + return d->nick; + } + return ""; +} + +const char* ModeParser::Revoke(userrec *d,chanrec *chan,int MASK) +{ + if (!chan) + return ""; + + UCListIter n = d->chans.find(chan); + if (n != d->chans.end()) + { + if ((n->second & MASK) == 0) + { + return ""; + } + n->second ^= MASK; + switch (MASK) + { + case UCMODE_OP: + n->first->DelOppedUser(d); + break; + case UCMODE_HOP: + n->first->DelHalfoppedUser(d); + break; + case UCMODE_VOICE: + n->first->DelVoicedUser(d); + break; + } + return d->nick; + } + return ""; +} + +void ModeParser::DisplayCurrentModes(userrec *user, userrec* targetuser, chanrec* targetchannel, const char* text) +{ + if (targetchannel) + { + /* Display channel's current mode string */ + user->WriteServ("324 %s %s +%s",user->nick, targetchannel->name, targetchannel->ChanModes(targetchannel->HasUser(user))); + user->WriteServ("329 %s %s %lu", user->nick, targetchannel->name, (unsigned long)targetchannel->age); + return; + } + else if (targetuser) + { + if (targetuser->Visibility && !targetuser->Visibility->VisibleTo(user)) + { + user->WriteServ("401 %s %s :No such nick/channel",user->nick, text); + return; + } + + if ((targetuser == user) || (IS_OPER(user))) + { + /* Display user's current mode string */ + user->WriteServ("221 %s :+%s",targetuser->nick,targetuser->FormatModes()); + if (IS_OPER(targetuser)) + user->WriteServ("008 %s +%s :Server notice mask", targetuser->nick, targetuser->FormatNoticeMasks()); + return; + } + else + { + user->WriteServ("502 %s :Can't change mode for other users", user->nick); + return; + } + } + + /* No such nick/channel */ + user->WriteServ("401 %s %s :No such nick/channel",user->nick, text); + return; +} + +void ModeParser::Process(const char** parameters, int pcnt, userrec *user, bool servermode) +{ + std::string target = parameters[0]; + ModeType type = MODETYPE_USER; + unsigned char mask = 0; + chanrec* targetchannel = ServerInstance->FindChan(parameters[0]); + userrec* targetuser = ServerInstance->FindNick(parameters[0]); + + LastParse.clear(); + + /* Special case for displaying the list for listmodes, + * e.g. MODE #chan b, or MODE #chan +b without a parameter + */ + if ((targetchannel) && (pcnt == 2)) + { + const char* mode = parameters[1]; + int nonlistmodes_found = 0; + bool sent[256]; + + mask = MASK_CHANNEL; + + memset(&sent, 0, 256); + + while (mode && *mode) + { + unsigned char mletter = *mode; + + if (*mode == '+') + { + mode++; + continue; + } + + /* Ensure the user doesnt request the same mode twice, + * so they cant flood themselves off out of idiocy. + */ + if (!sent[mletter]) + { + sent[mletter] = true; + } + else + { + mode++; + continue; + } + + ModeHandler *mh = this->FindMode(*mode, MODETYPE_CHANNEL); + bool display = true; + + if ((mh) && (mh->IsListMode())) + { + if (ServerInstance->Config->HideModeLists[mletter] && (targetchannel->GetStatus(user) < STATUS_HOP)) + { + user->WriteServ("482 %s %s :Only half-operators and above may view the +%c list",user->nick, targetchannel->name, *mode++); + continue; + } + + /** See below for a description of what craq this is :D + */ + unsigned char handler_id = (*mode - 65) | mask; + + for(ModeWatchIter watchers = modewatchers[handler_id].begin(); watchers != modewatchers[handler_id].end(); watchers++) + { + std::string dummyparam; + + if (!((*watchers)->BeforeMode(user, NULL, targetchannel, dummyparam, true, MODETYPE_CHANNEL))) + display = false; + } + + if (display) + mh->DisplayList(user, targetchannel); + } + else + nonlistmodes_found++; + + mode++; + } + + /* We didnt have any modes that were non-list, we can return here */ + if (!nonlistmodes_found) + return; + } + + if (pcnt == 1) + { + this->DisplayCurrentModes(user, targetuser, targetchannel, parameters[0]); + } + else if (pcnt > 1) + { + if (targetchannel) + { + type = MODETYPE_CHANNEL; + mask = MASK_CHANNEL; + + /* Extra security checks on channel modes + * (e.g. are they a (half)op? + */ + + if ((IS_LOCAL(user)) && (targetchannel->GetStatus(user) < STATUS_HOP)) + { + /* We don't have halfop */ + int MOD_RESULT = 0; + FOREACH_RESULT(I_OnAccessCheck,OnAccessCheck(user, NULL, targetchannel, AC_GENERAL_MODE)); + if (MOD_RESULT == ACR_DENY) + return; + + if (MOD_RESULT == ACR_DEFAULT) + { + /* Are we a uline or is it a servermode? */ + if ((!ServerInstance->ULine(user->server)) && (!servermode)) + { + /* Not enough permission: + * NOT a uline and NOT a servermode, + * OR, NOT halfop or above. + */ + user->WriteServ("482 %s %s :You're not a channel (half)operator",user->nick, targetchannel->name); + return; + } + } + } + } + else if (targetuser) + { + type = MODETYPE_USER; + mask = MASK_USER; + if ((user != targetuser) && (!ServerInstance->ULine(user->server))) + { + user->WriteServ("502 %s :Can't change mode for other users", user->nick); + return; + } + } + else + { + /* No such nick/channel */ + user->WriteServ("401 %s %s :No such nick/channel",user->nick, parameters[0]); + return; + } + + std::string mode_sequence = parameters[1]; + std::string parameter; + std::ostringstream parameter_list; + std::string output_sequence; + bool adding = true, state_change = false; + unsigned char handler_id = 0; + int parameter_counter = 2; /* Index of first parameter */ + int parameter_count = 0; + bool last_successful_state_change = false; + + /* A mode sequence that doesnt start with + or -. Assume +. - Thanks for the suggestion spike (bug#132) */ + if ((*mode_sequence.begin() != '+') && (*mode_sequence.begin() != '-')) + mode_sequence.insert(0, "+"); + + for (std::string::const_iterator letter = mode_sequence.begin(); letter != mode_sequence.end(); letter++) + { + unsigned char modechar = *letter; + + switch (modechar) + { + /* NB: + * For + and - mode characters, we don't just stick the character into the output sequence. + * This is because the user may do something dumb, like: +-+ooo or +oo-+. To prevent this + * appearing in the output sequence, we store a flag which says there was a state change, + * which is set on any + or -, however, the + or - that we finish on is only appended to + * the output stream in the event it is followed by a non "+ or -" character, such as o or v. + */ + case '+': + /* The following expression prevents: +o+o nick nick, compressing it to +oo nick nick, + * however, will allow the + if it is the first item in the sequence, regardless. + */ + if ((!adding) || (!output_sequence.length())) + state_change = true; + adding = true; + if (!output_sequence.length()) + last_successful_state_change = false; + continue; + break; + case '-': + if ((adding) || (!output_sequence.length())) + state_change = true; + adding = false; + if (!output_sequence.length()) + last_successful_state_change = true; + continue; + break; + default: + + /** + * Watch carefully for the sleight of hand trick. + * 65 is the ascii value of 'A'. We take this from + * the char we're looking at to get a number between + * 1 and 127. We then logic-or it to get the hashed + * position, dependent on wether its a channel or + * a user mode. This is a little stranger, but a lot + * faster, than using a map of pairs. + */ + handler_id = (modechar - 65) | mask; + + if (modehandlers[handler_id]) + { + bool abort = false; + + if (modehandlers[handler_id]->GetModeType() == type) + { + if (modehandlers[handler_id]->GetNumParams(adding)) + { + /* This mode expects a parameter, do we have any parameters left in our list to use? */ + if (parameter_counter < pcnt) + { + parameter = parameters[parameter_counter++]; + + /* Yerk, invalid! */ + if ((parameter.find(':') == 0) || (parameter.rfind(' ') != std::string::npos)) + parameter.clear(); + } + else + { + /* No parameter, continue to the next mode */ + continue; + } + + bool had_parameter = !parameter.empty(); + + for (ModeWatchIter watchers = modewatchers[handler_id].begin(); watchers != modewatchers[handler_id].end(); watchers++) + { + if ((*watchers)->BeforeMode(user, targetuser, targetchannel, parameter, adding, type) == false) + { + abort = true; + break; + } + /* A module whacked the parameter completely, and there was one. abort. */ + if ((had_parameter) && (parameter.empty())) + { + abort = true; + break; + } + } + + if (abort) + continue; + } + else + { + /* Fix by brain: mode watchers not being called for parameterless modes */ + for (ModeWatchIter watchers = modewatchers[handler_id].begin(); watchers != modewatchers[handler_id].end(); watchers++) + { + if ((*watchers)->BeforeMode(user, targetuser, targetchannel, parameter, adding, type) == false) + { + abort = true; + break; + } + } + + if (abort) + continue; + } + + /* It's an oper only mode, check if theyre an oper. If they arent, + * eat any parameter that came with the mode, and continue to next + */ + if ((IS_LOCAL(user)) && (modehandlers[handler_id]->NeedsOper()) && (!IS_OPER(user))) + { + user->WriteServ("481 %s :Permission Denied - Only IRC operators may %sset %s mode %c", user->nick, + adding ? "" : "un", type == MODETYPE_CHANNEL ? "channel" : "user", + modehandlers[handler_id]->GetModeChar()); + continue; + } + + /* Call the handler for the mode */ + ModeAction ma = modehandlers[handler_id]->OnModeChange(user, targetuser, targetchannel, parameter, adding); + + if ((modehandlers[handler_id]->GetNumParams(adding)) && (parameter.empty())) + { + /* The handler nuked the parameter and they are supposed to have one. + * We CANT continue now, even if they actually returned MODEACTION_ALLOW, + * so we bail to the next mode character. + */ + continue; + } + + if (ma == MODEACTION_ALLOW) + { + /* We're about to output a valid mode letter - was there previously a pending state-change? */ + if (state_change) + { + if (adding != last_successful_state_change) + output_sequence.append(adding ? "+" : "-"); + last_successful_state_change = adding; + } + + /* Add the mode letter */ + output_sequence.push_back(modechar); + + /* Is there a valid parameter for this mode? If so add it to the parameter list */ + if ((modehandlers[handler_id]->GetNumParams(adding)) && (!parameter.empty())) + { + parameter_list << " " << parameter; + parameter_count++; + /* Does this mode have a prefix? */ + if (modehandlers[handler_id]->GetPrefix() && targetchannel) + { + userrec* user_to_prefix = ServerInstance->FindNick(parameter); + if (user_to_prefix) + targetchannel->SetPrefix(user_to_prefix, modehandlers[handler_id]->GetPrefix(), + modehandlers[handler_id]->GetPrefixRank(), adding); + } + } + + /* Call all the AfterMode events in the mode watchers for this mode */ + for (ModeWatchIter watchers = modewatchers[handler_id].begin(); watchers != modewatchers[handler_id].end(); watchers++) + (*watchers)->AfterMode(user, targetuser, targetchannel, parameter, adding, type); + + /* Reset the state change flag */ + state_change = false; + + if ((output_sequence.length() + parameter_list.str().length() > 450) || (output_sequence.length() > 100) + || (parameter_count > MAXMODES)) + { + /* We cant have a mode sequence this long */ + letter = mode_sequence.end() - 1; + continue; + } + } + } + } + else + { + /* No mode handler? Unknown mode character then. */ + user->WriteServ("472 %s %c :is unknown mode char to me",user->nick, modechar); + } + break; + } + } + + /* Was there at least one valid mode in the sequence? */ + if (!output_sequence.empty()) + { + if (servermode) + { + if (type == MODETYPE_CHANNEL) + { + targetchannel->WriteChannelWithServ(ServerInstance->Config->ServerName, "MODE %s %s%s", targetchannel->name, output_sequence.c_str(), parameter_list.str().c_str()); + this->LastParse = targetchannel->name; + } + else + { + targetuser->WriteServ("MODE %s %s%s",targetuser->nick,output_sequence.c_str(), parameter_list.str().c_str()); + this->LastParse = targetuser->nick; + } + } + else + { + if (type == MODETYPE_CHANNEL) + { + targetchannel->WriteChannel(user,"MODE %s %s%s",targetchannel->name,output_sequence.c_str(),parameter_list.str().c_str()); + FOREACH_MOD(I_OnMode,OnMode(user, targetchannel, TYPE_CHANNEL, output_sequence + parameter_list.str())); + this->LastParse = targetchannel->name; + } + else + { + user->WriteTo(targetuser,"MODE %s %s%s",targetuser->nick,output_sequence.c_str(), parameter_list.str().c_str()); + FOREACH_MOD(I_OnMode,OnMode(user, targetuser, TYPE_USER, output_sequence + parameter_list.str())); + this->LastParse = targetuser->nick; + } + } + + LastParse.append(" "); + LastParse.append(output_sequence); + LastParse.append(parameter_list.str()); + } + } +} + +const std::string& ModeParser::GetLastParse() +{ + return LastParse; +} + +void ModeParser::CleanMask(std::string &mask) +{ + std::string::size_type pos_of_pling = mask.find_first_of('!'); + std::string::size_type pos_of_at = mask.find_first_of('@'); + std::string::size_type pos_of_dot = mask.find_first_of('.'); + std::string::size_type pos_of_colon = mask.find_first_of(':'); /* Because ipv6 addresses are colon delimited */ + + if ((pos_of_pling == std::string::npos) && (pos_of_at == std::string::npos)) + { + /* Just a nick, or just a host */ + if ((pos_of_dot == std::string::npos) && (pos_of_colon == std::string::npos)) + { + /* It has no '.' in it, it must be a nick. */ + mask.append("!*@*"); + } + else + { + /* Got a dot in it? Has to be a host */ + mask = "*!*@" + mask; + } + } + else if ((pos_of_pling == std::string::npos) && (pos_of_at != std::string::npos)) + { + /* Has an @ but no !, its a user@host */ + mask = "*!" + mask; + } + else if ((pos_of_pling != std::string::npos) && (pos_of_at == std::string::npos)) + { + /* Has a ! but no @, it must be a nick!ident */ + mask.append("@*"); + } +} + +bool ModeParser::AddMode(ModeHandler* mh, unsigned const char modeletter) +{ + unsigned char mask = 0; + unsigned char pos = 0; + + /* Yes, i know, this might let people declare modes like '_' or '^'. + * If they do that, thats their problem, and if i ever EVER see an + * official InspIRCd developer do that, i'll beat them with a paddle! + */ + if ((mh->GetModeChar() < 'A') || (mh->GetModeChar() > 'z') || (mh->GetPrefix() > 126)) + return false; + + /* A mode prefix of ',' is not acceptable, it would fuck up server to server. + * A mode prefix of ':' will fuck up both server to server, and client to server. + * A mode prefix of '#' will mess up /whois and /privmsg + */ + if ((mh->GetPrefix() == ',') || (mh->GetPrefix() == ':') || (mh->GetPrefix() == '#')) + return false; + + mh->GetModeType() == MODETYPE_USER ? mask = MASK_USER : mask = MASK_CHANNEL; + pos = (mh->GetModeChar()-65) | mask; + + if (modehandlers[pos]) + return false; + + modehandlers[pos] = mh; + return true; +} + +bool ModeParser::DelMode(ModeHandler* mh) +{ + unsigned char mask = 0; + unsigned char pos = 0; + + if ((mh->GetModeChar() < 'A') || (mh->GetModeChar() > 'z')) + return false; + + mh->GetModeType() == MODETYPE_USER ? mask = MASK_USER : mask = MASK_CHANNEL; + pos = (mh->GetModeChar()-65) | mask; + + if (!modehandlers[pos]) + return false; + + switch (mh->GetModeType()) + { + case MODETYPE_USER: + for (user_hash::iterator i = ServerInstance->clientlist->begin(); i != ServerInstance->clientlist->end(); i++) + { + mh->RemoveMode(i->second); + } + break; + case MODETYPE_CHANNEL: + for (chan_hash::iterator i = ServerInstance->chanlist->begin(); i != ServerInstance->chanlist->end(); i++) + { + mh->RemoveMode(i->second); + } + break; + } + + modehandlers[pos] = NULL; + + return true; +} + +ModeHandler* ModeParser::FindMode(unsigned const char modeletter, ModeType mt) +{ + unsigned char mask = 0; + unsigned char pos = 0; + + if ((modeletter < 'A') || (modeletter > 'z')) + return NULL; + + mt == MODETYPE_USER ? mask = MASK_USER : mask = MASK_CHANNEL; + pos = (modeletter-65) | mask; + + return modehandlers[pos]; +} + +std::string ModeParser::UserModeList() +{ + char modestr[256]; + int pointer = 0; + + for (unsigned char mode = 'A'; mode <= 'z'; mode++) + { + unsigned char pos = (mode-65) | MASK_USER; + + if (modehandlers[pos]) + modestr[pointer++] = mode; + } + modestr[pointer++] = 0; + return modestr; +} + +std::string ModeParser::ChannelModeList() +{ + char modestr[256]; + int pointer = 0; + + for (unsigned char mode = 'A'; mode <= 'z'; mode++) + { + if ((!ServerInstance->Config->AllowHalfop) && (mode == 'h')) + continue; + + unsigned char pos = (mode-65) | MASK_CHANNEL; + + if (modehandlers[pos]) + modestr[pointer++] = mode; + } + modestr[pointer++] = 0; + return modestr; +} + +std::string ModeParser::ParaModeList() +{ + char modestr[256]; + int pointer = 0; + + for (unsigned char mode = 'A'; mode <= 'z'; mode++) + { + if ((!ServerInstance->Config->AllowHalfop) && (mode == 'h')) + continue; + + unsigned char pos = (mode-65) | MASK_CHANNEL; + + if ((modehandlers[pos]) && (modehandlers[pos]->GetNumParams(true))) + modestr[pointer++] = mode; + } + modestr[pointer++] = 0; + return modestr; +} + +ModeHandler* ModeParser::FindPrefix(unsigned const char pfxletter) +{ + for (unsigned char mode = 'A'; mode <= 'z'; mode++) + { + unsigned char pos = (mode-65) | MASK_CHANNEL; + + if ((modehandlers[pos]) && (modehandlers[pos]->GetPrefix() == pfxletter)) + { + return modehandlers[pos]; + } + } + return NULL; +} + +std::string ModeParser::ModeString(userrec* user, chanrec* channel) +{ + std::string types; + std::string pars; + + if (!channel || !user) + return ""; + + for (unsigned char mode = 'A'; mode <= 'z'; mode++) + { + unsigned char pos = (mode-65) | MASK_CHANNEL; + ModeHandler* mh = modehandlers[pos]; + if ((mh) && (mh->GetNumParams(true)) && (mh->GetNumParams(false))) + { + ModePair ret; + ret = mh->ModeSet(NULL, user, channel, user->nick); + if ((ret.first) && (ret.second == user->nick)) + { + pars.append(" "); + pars.append(user->nick); + types.push_back(mh->GetModeChar()); + } + } + } + + return types+pars; +} + +std::string ModeParser::ChanModes() +{ + std::string type1; /* Listmodes EXCEPT those with a prefix */ + std::string type2; /* Modes that take a param when adding or removing */ + std::string type3; /* Modes that only take a param when adding */ + std::string type4; /* Modes that dont take a param */ + + for (unsigned char mode = 'A'; mode <= 'z'; mode++) + { + if ((!ServerInstance->Config->AllowHalfop) && (mode == 'h')) + continue; + + unsigned char pos = (mode-65) | MASK_CHANNEL; + /* One parameter when adding */ + if (modehandlers[pos]) + { + if (modehandlers[pos]->GetNumParams(true)) + { + if ((modehandlers[pos]->IsListMode()) && (!modehandlers[pos]->GetPrefix())) + { + type1 += modehandlers[pos]->GetModeChar(); + } + else + { + /* ... and one parameter when removing */ + if (modehandlers[pos]->GetNumParams(false)) + { + /* But not a list mode */ + if (!modehandlers[pos]->GetPrefix()) + { + type2 += modehandlers[pos]->GetModeChar(); + } + } + else + { + /* No parameters when removing */ + type3 += modehandlers[pos]->GetModeChar(); + } + } + } + else + { + type4 += modehandlers[pos]->GetModeChar(); + } + } + + } + + return type1 + "," + type2 + "," + type3 + "," + type4; +} + +bool ModeParser::PrefixComparison(prefixtype one, prefixtype two) +{ + return one.second > two.second; +} + +std::string ModeParser::BuildPrefixes() +{ + std::string mletters; + std::string mprefixes; + pfxcontainer pfx; + std::map<char,char> prefix_to_mode; + + for (unsigned char mode = 'A'; mode <= 'z'; mode++) + { + if ((!ServerInstance->Config->AllowHalfop) && (mode == 'h')) + continue; + + unsigned char pos = (mode-65) | MASK_CHANNEL; + + if ((modehandlers[pos]) && (modehandlers[pos]->GetPrefix())) + { + pfx.push_back(std::make_pair<char,unsigned int>(modehandlers[pos]->GetPrefix(), modehandlers[pos]->GetPrefixRank())); + prefix_to_mode[modehandlers[pos]->GetPrefix()] = modehandlers[pos]->GetModeChar(); + } + } + + sort(pfx.begin(), pfx.end(), ModeParser::PrefixComparison); + + for (pfxcontainer::iterator n = pfx.begin(); n != pfx.end(); n++) + { + mletters = mletters + n->first; + mprefixes = mprefixes + prefix_to_mode.find(n->first)->second; + } + + return "(" + mprefixes + ")" + mletters; +} + +bool ModeParser::AddModeWatcher(ModeWatcher* mw) +{ + unsigned char mask = 0; + unsigned char pos = 0; + + if (!mw) + return false; + + if ((mw->GetModeChar() < 'A') || (mw->GetModeChar() > 'z')) + return false; + + mw->GetModeType() == MODETYPE_USER ? mask = MASK_USER : mask = MASK_CHANNEL; + pos = (mw->GetModeChar()-65) | mask; + + modewatchers[pos].push_back(mw); + + return true; +} + +bool ModeParser::DelModeWatcher(ModeWatcher* mw) +{ + unsigned char mask = 0; + unsigned char pos = 0; + + if (!mw) + return false; + + if ((mw->GetModeChar() < 'A') || (mw->GetModeChar() > 'z')) + return false; + + mw->GetModeType() == MODETYPE_USER ? mask = MASK_USER : mask = MASK_CHANNEL; + pos = (mw->GetModeChar()-65) | mask; + + ModeWatchIter a = find(modewatchers[pos].begin(),modewatchers[pos].end(),mw); + + if (a == modewatchers[pos].end()) + { + return false; + } + + modewatchers[pos].erase(a); + + return true; +} + +/** This default implementation can remove simple user modes + */ +void ModeHandler::RemoveMode(userrec* user) +{ + char moderemove[MAXBUF]; + const char* parameters[] = { user->nick, moderemove }; + + if (user->IsModeSet(this->GetModeChar())) + { + sprintf(moderemove,"-%c",this->GetModeChar()); + ServerInstance->Parser->CallHandler("MODE", parameters, 2, user); + } +} + +/** This default implementation can remove simple channel modes + * (no parameters) + */ +void ModeHandler::RemoveMode(chanrec* channel) +{ + char moderemove[MAXBUF]; + const char* parameters[] = { channel->name, moderemove }; + + if (channel->IsModeSet(this->GetModeChar())) + { + userrec* n = new userrec(ServerInstance); + + sprintf(moderemove,"-%c",this->GetModeChar()); + n->SetFd(FD_MAGIC_NUMBER); + + ServerInstance->SendMode(parameters, 2, n); + + delete n; + } +} + +ModeParser::ModeParser(InspIRCd* Instance) : ServerInstance(Instance) +{ + struct Initializer + { + char modechar; + ModeHandler* handler; + }; + + Initializer modes[] = { + { 's', new ModeChannelSecret(Instance) }, + { 'p', new ModeChannelPrivate(Instance) }, + { 'm', new ModeChannelModerated(Instance) }, + { 't', new ModeChannelTopicOps(Instance) }, + { 'n', new ModeChannelNoExternal(Instance) }, + { 'i', new ModeChannelInviteOnly(Instance) }, + { 'k', new ModeChannelKey(Instance) }, + { 'l', new ModeChannelLimit(Instance) }, + { 'b', new ModeChannelBan(Instance) }, + { 'o', new ModeChannelOp(Instance) }, + { 'h', new ModeChannelHalfOp(Instance) }, + { 'v', new ModeChannelVoice(Instance) }, + { 's', new ModeUserServerNotice(Instance) }, + { 'w', new ModeUserWallops(Instance) }, + { 'i', new ModeUserInvisible(Instance) }, + { 'o', new ModeUserOperator(Instance) }, + { 'n', new ModeUserServerNoticeMask(Instance) }, + { 0, NULL } + }; + + /* Clear mode list */ + memset(modehandlers, 0, sizeof(modehandlers)); + memset(modewatchers, 0, sizeof(modewatchers)); + + /* Last parse string */ + LastParse.clear(); + + /* Initialise the RFC mode letters */ + for (int index = 0; modes[index].modechar; index++) + this->AddMode(modes[index].handler, modes[index].modechar); +} diff --git a/src/modes/Makefile b/src/modes/Makefile index 1b7743649..8839168b7 100644 --- a/src/modes/Makefile +++ b/src/modes/Makefile @@ -1 +1,65 @@ -CC = i am cornholio
CXXFLAGS = -I../../include ${FLAGS}
all: umode_w.o umode_s.o umode_o.o umode_n.o umode_i.o cmode_v.o cmode_t.o cmode_s.o cmode_p.o cmode_o.o cmode_n.o cmode_m.o cmode_l.o cmode_k.o cmode_i.o cmode_h.o cmode_b.o modeclasses.a
umode_w.o: umode_w.cpp ../../include/base.h ../../include/modules.h ../../include/inspircd.h ../../include/channels.h ../../include/users.h ../../include/inspircd_config.h ../../include/mode.h
$(CC) -pipe -I../../include $(FLAGS) -export-dynamic -c umode_w.cpp
umode_s.o: umode_s.cpp ../../include/base.h ../../include/modules.h ../../include/inspircd.h ../../include/channels.h ../../include/users.h ../../include/inspircd_config.h ../../include/mode.h
$(CC) -pipe -I../../include $(FLAGS) -export-dynamic -c umode_s.cpp
umode_o.o: umode_o.cpp ../../include/base.h ../../include/modules.h ../../include/inspircd.h ../../include/channels.h ../../include/users.h ../../include/inspircd_config.h ../../include/mode.h
$(CC) -pipe -I../../include $(FLAGS) -export-dynamic -c umode_o.cpp
umode_n.o: umode_n.cpp ../../include/base.h ../../include/modules.h ../../include/inspircd.h ../../include/channels.h ../../include/users.h ../../include/inspircd_config.h ../../include/mode.h
$(CC) -pipe -I../../include $(FLAGS) -export-dynamic -c umode_n.cpp
umode_i.o: umode_i.cpp ../../include/base.h ../../include/modules.h ../../include/inspircd.h ../../include/channels.h ../../include/users.h ../../include/inspircd_config.h ../../include/mode.h
$(CC) -pipe -I../../include $(FLAGS) -export-dynamic -c umode_i.cpp
cmode_v.o: cmode_v.cpp ../../include/base.h ../../include/modules.h ../../include/inspircd.h ../../include/channels.h ../../include/users.h ../../include/inspircd_config.h ../../include/mode.h
$(CC) -pipe -I../../include $(FLAGS) -export-dynamic -c cmode_v.cpp
cmode_t.o: cmode_t.cpp ../../include/base.h ../../include/modules.h ../../include/inspircd.h ../../include/channels.h ../../include/users.h ../../include/inspircd_config.h ../../include/mode.h
$(CC) -pipe -I../../include $(FLAGS) -export-dynamic -c cmode_t.cpp
cmode_s.o: cmode_s.cpp ../../include/base.h ../../include/modules.h ../../include/inspircd.h ../../include/channels.h ../../include/users.h ../../include/inspircd_config.h ../../include/mode.h
$(CC) -pipe -I../../include $(FLAGS) -export-dynamic -c cmode_s.cpp
cmode_p.o: cmode_p.cpp ../../include/base.h ../../include/modules.h ../../include/inspircd.h ../../include/channels.h ../../include/users.h ../../include/inspircd_config.h ../../include/mode.h
$(CC) -pipe -I../../include $(FLAGS) -export-dynamic -c cmode_p.cpp
cmode_o.o: cmode_o.cpp ../../include/base.h ../../include/modules.h ../../include/inspircd.h ../../include/channels.h ../../include/users.h ../../include/inspircd_config.h ../../include/mode.h
$(CC) -pipe -I../../include $(FLAGS) -export-dynamic -c cmode_o.cpp
cmode_n.o: cmode_n.cpp ../../include/base.h ../../include/modules.h ../../include/inspircd.h ../../include/channels.h ../../include/users.h ../../include/inspircd_config.h ../../include/mode.h
$(CC) -pipe -I../../include $(FLAGS) -export-dynamic -c cmode_n.cpp
cmode_m.o: cmode_m.cpp ../../include/base.h ../../include/modules.h ../../include/inspircd.h ../../include/channels.h ../../include/users.h ../../include/inspircd_config.h ../../include/mode.h
$(CC) -pipe -I../../include $(FLAGS) -export-dynamic -c cmode_m.cpp
cmode_l.o: cmode_l.cpp ../../include/base.h ../../include/modules.h ../../include/inspircd.h ../../include/channels.h ../../include/users.h ../../include/inspircd_config.h ../../include/mode.h
$(CC) -pipe -I../../include $(FLAGS) -export-dynamic -c cmode_l.cpp
cmode_k.o: cmode_k.cpp ../../include/base.h ../../include/modules.h ../../include/inspircd.h ../../include/channels.h ../../include/users.h ../../include/inspircd_config.h ../../include/mode.h
$(CC) -pipe -I../../include $(FLAGS) -export-dynamic -c cmode_k.cpp
cmode_i.o: cmode_i.cpp ../../include/base.h ../../include/modules.h ../../include/inspircd.h ../../include/channels.h ../../include/users.h ../../include/inspircd_config.h ../../include/mode.h
$(CC) -pipe -I../../include $(FLAGS) -export-dynamic -c cmode_i.cpp
cmode_h.o: cmode_h.cpp ../../include/base.h ../../include/modules.h ../../include/inspircd.h ../../include/channels.h ../../include/users.h ../../include/inspircd_config.h ../../include/mode.h
$(CC) -pipe -I../../include $(FLAGS) -export-dynamic -c cmode_h.cpp
cmode_b.o: cmode_b.cpp ../../include/base.h ../../include/modules.h ../../include/inspircd.h ../../include/channels.h ../../include/users.h ../../include/inspircd_config.h ../../include/mode.h
$(CC) -pipe -I../../include $(FLAGS) -export-dynamic -c cmode_b.cpp
modeclasses.a: umode_w.o umode_s.o umode_o.o umode_n.o umode_i.o cmode_v.o cmode_t.o cmode_s.o cmode_p.o cmode_o.o cmode_n.o cmode_m.o cmode_l.o cmode_k.o cmode_i.o cmode_h.o cmode_b.o
@-rm -rf modeclasses.a
ar r modeclasses.a *.o
ranlib modeclasses.a
clean:
@-rm *.o
@-rm modeclasses.a
\ No newline at end of file +CC = i am cornholio +CXXFLAGS = -I../../include ${FLAGS} + +all: umode_w.o umode_s.o umode_o.o umode_n.o umode_i.o cmode_v.o cmode_t.o cmode_s.o cmode_p.o cmode_o.o cmode_n.o cmode_m.o cmode_l.o cmode_k.o cmode_i.o cmode_h.o cmode_b.o modeclasses.a + +umode_w.o: umode_w.cpp ../../include/base.h ../../include/modules.h ../../include/inspircd.h ../../include/channels.h ../../include/users.h ../../include/inspircd_config.h ../../include/mode.h + $(CC) -pipe -I../../include $(FLAGS) -export-dynamic -c umode_w.cpp + +umode_s.o: umode_s.cpp ../../include/base.h ../../include/modules.h ../../include/inspircd.h ../../include/channels.h ../../include/users.h ../../include/inspircd_config.h ../../include/mode.h + $(CC) -pipe -I../../include $(FLAGS) -export-dynamic -c umode_s.cpp + +umode_o.o: umode_o.cpp ../../include/base.h ../../include/modules.h ../../include/inspircd.h ../../include/channels.h ../../include/users.h ../../include/inspircd_config.h ../../include/mode.h + $(CC) -pipe -I../../include $(FLAGS) -export-dynamic -c umode_o.cpp + +umode_n.o: umode_n.cpp ../../include/base.h ../../include/modules.h ../../include/inspircd.h ../../include/channels.h ../../include/users.h ../../include/inspircd_config.h ../../include/mode.h + $(CC) -pipe -I../../include $(FLAGS) -export-dynamic -c umode_n.cpp + +umode_i.o: umode_i.cpp ../../include/base.h ../../include/modules.h ../../include/inspircd.h ../../include/channels.h ../../include/users.h ../../include/inspircd_config.h ../../include/mode.h + $(CC) -pipe -I../../include $(FLAGS) -export-dynamic -c umode_i.cpp + +cmode_v.o: cmode_v.cpp ../../include/base.h ../../include/modules.h ../../include/inspircd.h ../../include/channels.h ../../include/users.h ../../include/inspircd_config.h ../../include/mode.h + $(CC) -pipe -I../../include $(FLAGS) -export-dynamic -c cmode_v.cpp + +cmode_t.o: cmode_t.cpp ../../include/base.h ../../include/modules.h ../../include/inspircd.h ../../include/channels.h ../../include/users.h ../../include/inspircd_config.h ../../include/mode.h + $(CC) -pipe -I../../include $(FLAGS) -export-dynamic -c cmode_t.cpp + +cmode_s.o: cmode_s.cpp ../../include/base.h ../../include/modules.h ../../include/inspircd.h ../../include/channels.h ../../include/users.h ../../include/inspircd_config.h ../../include/mode.h + $(CC) -pipe -I../../include $(FLAGS) -export-dynamic -c cmode_s.cpp + +cmode_p.o: cmode_p.cpp ../../include/base.h ../../include/modules.h ../../include/inspircd.h ../../include/channels.h ../../include/users.h ../../include/inspircd_config.h ../../include/mode.h + $(CC) -pipe -I../../include $(FLAGS) -export-dynamic -c cmode_p.cpp + +cmode_o.o: cmode_o.cpp ../../include/base.h ../../include/modules.h ../../include/inspircd.h ../../include/channels.h ../../include/users.h ../../include/inspircd_config.h ../../include/mode.h + $(CC) -pipe -I../../include $(FLAGS) -export-dynamic -c cmode_o.cpp + +cmode_n.o: cmode_n.cpp ../../include/base.h ../../include/modules.h ../../include/inspircd.h ../../include/channels.h ../../include/users.h ../../include/inspircd_config.h ../../include/mode.h + $(CC) -pipe -I../../include $(FLAGS) -export-dynamic -c cmode_n.cpp + +cmode_m.o: cmode_m.cpp ../../include/base.h ../../include/modules.h ../../include/inspircd.h ../../include/channels.h ../../include/users.h ../../include/inspircd_config.h ../../include/mode.h + $(CC) -pipe -I../../include $(FLAGS) -export-dynamic -c cmode_m.cpp + +cmode_l.o: cmode_l.cpp ../../include/base.h ../../include/modules.h ../../include/inspircd.h ../../include/channels.h ../../include/users.h ../../include/inspircd_config.h ../../include/mode.h + $(CC) -pipe -I../../include $(FLAGS) -export-dynamic -c cmode_l.cpp + +cmode_k.o: cmode_k.cpp ../../include/base.h ../../include/modules.h ../../include/inspircd.h ../../include/channels.h ../../include/users.h ../../include/inspircd_config.h ../../include/mode.h + $(CC) -pipe -I../../include $(FLAGS) -export-dynamic -c cmode_k.cpp + +cmode_i.o: cmode_i.cpp ../../include/base.h ../../include/modules.h ../../include/inspircd.h ../../include/channels.h ../../include/users.h ../../include/inspircd_config.h ../../include/mode.h + $(CC) -pipe -I../../include $(FLAGS) -export-dynamic -c cmode_i.cpp + +cmode_h.o: cmode_h.cpp ../../include/base.h ../../include/modules.h ../../include/inspircd.h ../../include/channels.h ../../include/users.h ../../include/inspircd_config.h ../../include/mode.h + $(CC) -pipe -I../../include $(FLAGS) -export-dynamic -c cmode_h.cpp + +cmode_b.o: cmode_b.cpp ../../include/base.h ../../include/modules.h ../../include/inspircd.h ../../include/channels.h ../../include/users.h ../../include/inspircd_config.h ../../include/mode.h + $(CC) -pipe -I../../include $(FLAGS) -export-dynamic -c cmode_b.cpp + +modeclasses.a: umode_w.o umode_s.o umode_o.o umode_n.o umode_i.o cmode_v.o cmode_t.o cmode_s.o cmode_p.o cmode_o.o cmode_n.o cmode_m.o cmode_l.o cmode_k.o cmode_i.o cmode_h.o cmode_b.o + @-rm -rf modeclasses.a + ar r modeclasses.a *.o + ranlib modeclasses.a + +clean: + @-rm *.o + @-rm modeclasses.a + diff --git a/src/modes/cmode_b.cpp b/src/modes/cmode_b.cpp index dbc2e925d..e306f31f6 100644 --- a/src/modes/cmode_b.cpp +++ b/src/modes/cmode_b.cpp @@ -1 +1,185 @@ -/* +------------------------------------+
* | 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 <string>
#include <vector>
#include "inspircd_config.h"
#include "configreader.h"
#include "hash_map.h"
#include "mode.h"
#include "channels.h"
#include "users.h"
#include "modules.h"
#include "inspstring.h"
#include "hashcomp.h"
#include "modes/cmode_b.h"
ModeChannelBan::ModeChannelBan(InspIRCd* Instance) : ModeHandler(Instance, 'b', 1, 1, true, MODETYPE_CHANNEL, false)
{
}
ModeAction ModeChannelBan::OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string ¶meter, bool adding)
{
int status = channel->GetStatus(source);
/* Call the correct method depending on wether we're adding or removing the mode */
if (adding)
{
parameter = this->AddBan(source, parameter, channel, status);
}
else
{
parameter = this->DelBan(source, parameter, channel, status);
}
/* If the method above 'ate' the parameter by reducing it to an empty string, then
* it won't matter wether we return ALLOW or DENY here, as an empty string overrides
* the return value and is always MODEACTION_DENY if the mode is supposed to have
* a parameter.
*/
return MODEACTION_ALLOW;
}
void ModeChannelBan::RemoveMode(chanrec* channel)
{
BanList copy;
char moderemove[MAXBUF];
userrec* n = new userrec(ServerInstance);
n->SetFd(FD_MAGIC_NUMBER);
for (BanList::iterator i = channel->bans.begin(); i != channel->bans.end(); i++)
{
copy.push_back(*i);
}
for (BanList::iterator i = copy.begin(); i != copy.end(); i++)
{
sprintf(moderemove,"-%c",this->GetModeChar());
const char* parameters[] = { channel->name, moderemove, i->data };
ServerInstance->SendMode(parameters, 3, n);
}
delete n;
}
void ModeChannelBan::RemoveMode(userrec* user)
{
}
void ModeChannelBan::DisplayList(userrec* user, chanrec* channel)
{
/* Display the channel banlist */
for (BanList::reverse_iterator i = channel->bans.rbegin(); i != channel->bans.rend(); ++i)
{
user->WriteServ("367 %s %s %s %s %d",user->nick, channel->name, i->data, i->set_by, i->set_time);
}
user->WriteServ("368 %s %s :End of channel ban list",user->nick, channel->name);
return;
}
std::string& ModeChannelBan::AddBan(userrec *user,std::string &dest,chanrec *chan,int status)
{
if ((!user) || (!chan))
{
ServerInstance->Log(DEFAULT,"*** BUG *** AddBan was given an invalid parameter");
dest = "";
return dest;
}
/* Attempt to tidy the mask */
ModeParser::CleanMask(dest);
/* If the mask was invalid, we exit */
if (dest == "")
return dest;
long maxbans = chan->GetMaxBans();
if ((unsigned)chan->bans.size() > (unsigned)maxbans)
{
user->WriteServ("478 %s %s :Channel ban list for %s is full (maximum entries for this channel is %d)",user->nick, chan->name,chan->name,maxbans);
dest = "";
return dest;
}
int MOD_RESULT = 0;
FOREACH_RESULT(I_OnAddBan,OnAddBan(user,chan,dest));
if (MOD_RESULT)
{
dest = "";
return dest;
}
for (BanList::iterator i = chan->bans.begin(); i != chan->bans.end(); i++)
{
if (!strcasecmp(i->data,dest.c_str()))
{
/* dont allow a user to set the same ban twice */
dest = "";
return dest;
}
}
b.set_time = ServerInstance->Time();
strlcpy(b.data,dest.c_str(),MAXBUF);
if (*user->nick)
{
strlcpy(b.set_by,user->nick,NICKMAX-1);
}
else
{
strlcpy(b.set_by,ServerInstance->Config->ServerName,NICKMAX-1);
}
chan->bans.push_back(b);
return dest;
}
ModePair ModeChannelBan::ModeSet(userrec* source, userrec* dest, chanrec* channel, const std::string ¶meter)
{
for (BanList::iterator i = channel->bans.begin(); i != channel->bans.end(); i++)
{
if (!strcasecmp(i->data,parameter.c_str()))
{
return std::make_pair(true, i->data);
}
}
return std::make_pair(false, parameter);
}
std::string& ModeChannelBan::DelBan(userrec *user,std::string& dest,chanrec *chan,int status)
{
if ((!user) || (!chan))
{
ServerInstance->Log(DEFAULT,"*** BUG *** TakeBan was given an invalid parameter");
dest = "";
return dest;
}
/* 'Clean' the mask, e.g. nick -> nick!*@* */
ModeParser::CleanMask(dest);
for (BanList::iterator i = chan->bans.begin(); i != chan->bans.end(); i++)
{
if (!strcasecmp(i->data,dest.c_str()))
{
int MOD_RESULT = 0;
FOREACH_RESULT(I_OnDelBan,OnDelBan(user,chan,dest));
if (MOD_RESULT)
{
dest = "";
return dest;
}
chan->bans.erase(i);
return dest;
}
}
dest = "";
return dest;
}
\ No newline at end of file +/* +------------------------------------+ + * | 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 <string> +#include <vector> +#include "inspircd_config.h" +#include "configreader.h" +#include "hash_map.h" +#include "mode.h" +#include "channels.h" +#include "users.h" +#include "modules.h" +#include "inspstring.h" +#include "hashcomp.h" +#include "modes/cmode_b.h" + +ModeChannelBan::ModeChannelBan(InspIRCd* Instance) : ModeHandler(Instance, 'b', 1, 1, true, MODETYPE_CHANNEL, false) +{ +} + +ModeAction ModeChannelBan::OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string ¶meter, bool adding) +{ + int status = channel->GetStatus(source); + /* Call the correct method depending on wether we're adding or removing the mode */ + if (adding) + { + parameter = this->AddBan(source, parameter, channel, status); + } + else + { + parameter = this->DelBan(source, parameter, channel, status); + } + /* If the method above 'ate' the parameter by reducing it to an empty string, then + * it won't matter wether we return ALLOW or DENY here, as an empty string overrides + * the return value and is always MODEACTION_DENY if the mode is supposed to have + * a parameter. + */ + return MODEACTION_ALLOW; +} + +void ModeChannelBan::RemoveMode(chanrec* channel) +{ + BanList copy; + char moderemove[MAXBUF]; + userrec* n = new userrec(ServerInstance); + n->SetFd(FD_MAGIC_NUMBER); + + for (BanList::iterator i = channel->bans.begin(); i != channel->bans.end(); i++) + { + copy.push_back(*i); + } + for (BanList::iterator i = copy.begin(); i != copy.end(); i++) + { + sprintf(moderemove,"-%c",this->GetModeChar()); + const char* parameters[] = { channel->name, moderemove, i->data }; + ServerInstance->SendMode(parameters, 3, n); + } + + delete n; +} + +void ModeChannelBan::RemoveMode(userrec* user) +{ +} + +void ModeChannelBan::DisplayList(userrec* user, chanrec* channel) +{ + /* Display the channel banlist */ + for (BanList::reverse_iterator i = channel->bans.rbegin(); i != channel->bans.rend(); ++i) + { + user->WriteServ("367 %s %s %s %s %d",user->nick, channel->name, i->data, i->set_by, i->set_time); + } + user->WriteServ("368 %s %s :End of channel ban list",user->nick, channel->name); + return; +} + +std::string& ModeChannelBan::AddBan(userrec *user,std::string &dest,chanrec *chan,int status) +{ + if ((!user) || (!chan)) + { + ServerInstance->Log(DEFAULT,"*** BUG *** AddBan was given an invalid parameter"); + dest = ""; + return dest; + } + + /* Attempt to tidy the mask */ + ModeParser::CleanMask(dest); + /* If the mask was invalid, we exit */ + if (dest == "") + return dest; + + long maxbans = chan->GetMaxBans(); + if ((unsigned)chan->bans.size() > (unsigned)maxbans) + { + user->WriteServ("478 %s %s :Channel ban list for %s is full (maximum entries for this channel is %d)",user->nick, chan->name,chan->name,maxbans); + dest = ""; + return dest; + } + + int MOD_RESULT = 0; + FOREACH_RESULT(I_OnAddBan,OnAddBan(user,chan,dest)); + if (MOD_RESULT) + { + dest = ""; + return dest; + } + + for (BanList::iterator i = chan->bans.begin(); i != chan->bans.end(); i++) + { + if (!strcasecmp(i->data,dest.c_str())) + { + /* dont allow a user to set the same ban twice */ + dest = ""; + return dest; + } + } + + b.set_time = ServerInstance->Time(); + strlcpy(b.data,dest.c_str(),MAXBUF); + if (*user->nick) + { + strlcpy(b.set_by,user->nick,NICKMAX-1); + } + else + { + strlcpy(b.set_by,ServerInstance->Config->ServerName,NICKMAX-1); + } + chan->bans.push_back(b); + return dest; +} + +ModePair ModeChannelBan::ModeSet(userrec* source, userrec* dest, chanrec* channel, const std::string ¶meter) +{ + for (BanList::iterator i = channel->bans.begin(); i != channel->bans.end(); i++) + { + if (!strcasecmp(i->data,parameter.c_str())) + { + return std::make_pair(true, i->data); + } + } + return std::make_pair(false, parameter); +} + +std::string& ModeChannelBan::DelBan(userrec *user,std::string& dest,chanrec *chan,int status) +{ + if ((!user) || (!chan)) + { + ServerInstance->Log(DEFAULT,"*** BUG *** TakeBan was given an invalid parameter"); + dest = ""; + return dest; + } + + /* 'Clean' the mask, e.g. nick -> nick!*@* */ + ModeParser::CleanMask(dest); + + for (BanList::iterator i = chan->bans.begin(); i != chan->bans.end(); i++) + { + if (!strcasecmp(i->data,dest.c_str())) + { + int MOD_RESULT = 0; + FOREACH_RESULT(I_OnDelBan,OnDelBan(user,chan,dest)); + if (MOD_RESULT) + { + dest = ""; + return dest; + } + chan->bans.erase(i); + return dest; + } + } + dest = ""; + return dest; +} + diff --git a/src/modes/cmode_h.cpp b/src/modes/cmode_h.cpp index f2ffff665..ecee93388 100644 --- a/src/modes/cmode_h.cpp +++ b/src/modes/cmode_h.cpp @@ -1 +1,162 @@ -/* +------------------------------------+
* | 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 "mode.h"
#include "channels.h"
#include "users.h"
#include "modules.h"
#include "modes/cmode_h.h"
ModeChannelHalfOp::ModeChannelHalfOp(InspIRCd* Instance) : ModeHandler(Instance, 'h', 1, 1, true, MODETYPE_CHANNEL, false, '%')
{
}
unsigned int ModeChannelHalfOp::GetPrefixRank()
{
return HALFOP_VALUE;
}
ModePair ModeChannelHalfOp::ModeSet(userrec* source, userrec* dest, chanrec* channel, const std::string ¶meter)
{
userrec* x = ServerInstance->FindNick(parameter);
if (x)
{
if (channel->GetStatusFlags(x) & UCMODE_HOP)
{
return std::make_pair(true, x->nick);
}
else
{
return std::make_pair(false, parameter);
}
}
return std::make_pair(false, parameter);
}
void ModeChannelHalfOp::RemoveMode(chanrec* channel)
{
CUList* list = channel->GetHalfoppedUsers();
CUList copy;
char moderemove[MAXBUF];
userrec* n = new userrec(ServerInstance);
n->SetFd(FD_MAGIC_NUMBER);
for (CUList::iterator i = list->begin(); i != list->end(); i++)
{
userrec* n = i->first;
copy.insert(std::make_pair(n,n->nick));
}
for (CUList::iterator i = copy.begin(); i != copy.end(); i++)
{
sprintf(moderemove,"-%c",this->GetModeChar());
const char* parameters[] = { channel->name, moderemove, i->first->nick };
ServerInstance->SendMode(parameters, 3, n);
}
delete n;
}
void ModeChannelHalfOp::RemoveMode(userrec* user)
{
}
ModeAction ModeChannelHalfOp::OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string ¶meter, bool adding)
{
/* If halfops are not enabled in the conf, we don't execute
* anything in this class at all.
*/
if (!ServerInstance->Config->AllowHalfop)
{
parameter = "";
return MODEACTION_DENY;
}
int status = channel->GetStatus(source);
/* Call the correct method depending on wether we're adding or removing the mode */
if (adding)
{
parameter = this->AddHalfOp(source, parameter.c_str(), channel, status);
}
else
{
parameter = this->DelHalfOp(source, parameter.c_str(), channel, status);
}
/* If the method above 'ate' the parameter by reducing it to an empty string, then
* it won't matter wether we return ALLOW or DENY here, as an empty string overrides
* the return value and is always MODEACTION_DENY if the mode is supposed to have
* a parameter.
*/
if (parameter.length())
return MODEACTION_ALLOW;
else
return MODEACTION_DENY;
}
std::string ModeChannelHalfOp::AddHalfOp(userrec *user,const char* dest,chanrec *chan,int status)
{
userrec *d = ServerInstance->Modes->SanityChecks(user,dest,chan,status);
if (d)
{
if (IS_LOCAL(user))
{
int MOD_RESULT = 0;
FOREACH_RESULT(I_OnAccessCheck,OnAccessCheck(user,d,chan,AC_HALFOP));
if (MOD_RESULT == ACR_DENY)
return "";
if (MOD_RESULT == ACR_DEFAULT)
{
if ((status < STATUS_OP) && (!ServerInstance->ULine(user->server)))
{
user->WriteServ("482 %s %s :You're not a channel operator",user->nick, chan->name);
return "";
}
}
}
return ServerInstance->Modes->Grant(d,chan,UCMODE_HOP);
}
return "";
}
std::string ModeChannelHalfOp::DelHalfOp(userrec *user,const char *dest,chanrec *chan,int status)
{
userrec *d = ServerInstance->Modes->SanityChecks(user,dest,chan,status);
if (d)
{
if (IS_LOCAL(user))
{
int MOD_RESULT = 0;
FOREACH_RESULT(I_OnAccessCheck,OnAccessCheck(user,d,chan,AC_DEHALFOP));
if (MOD_RESULT == ACR_DENY)
return "";
if (MOD_RESULT == ACR_DEFAULT)
{
if ((user != d) && ((status < STATUS_OP) && (!ServerInstance->ULine(user->server))))
{
user->WriteServ("482 %s %s :You are not a channel operator",user->nick, chan->name);
return "";
}
}
}
return ServerInstance->Modes->Revoke(d,chan,UCMODE_HOP);
}
return "";
}
\ No newline at end of file +/* +------------------------------------+ + * | 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 "mode.h" +#include "channels.h" +#include "users.h" +#include "modules.h" +#include "modes/cmode_h.h" + +ModeChannelHalfOp::ModeChannelHalfOp(InspIRCd* Instance) : ModeHandler(Instance, 'h', 1, 1, true, MODETYPE_CHANNEL, false, '%') +{ +} + +unsigned int ModeChannelHalfOp::GetPrefixRank() +{ + return HALFOP_VALUE; +} + +ModePair ModeChannelHalfOp::ModeSet(userrec* source, userrec* dest, chanrec* channel, const std::string ¶meter) +{ + userrec* x = ServerInstance->FindNick(parameter); + if (x) + { + if (channel->GetStatusFlags(x) & UCMODE_HOP) + { + return std::make_pair(true, x->nick); + } + else + { + return std::make_pair(false, parameter); + } + } + return std::make_pair(false, parameter); +} + +void ModeChannelHalfOp::RemoveMode(chanrec* channel) +{ + CUList* list = channel->GetHalfoppedUsers(); + CUList copy; + char moderemove[MAXBUF]; + userrec* n = new userrec(ServerInstance); + n->SetFd(FD_MAGIC_NUMBER); + + for (CUList::iterator i = list->begin(); i != list->end(); i++) + { + userrec* n = i->first; + copy.insert(std::make_pair(n,n->nick)); + } + for (CUList::iterator i = copy.begin(); i != copy.end(); i++) + { + sprintf(moderemove,"-%c",this->GetModeChar()); + const char* parameters[] = { channel->name, moderemove, i->first->nick }; + ServerInstance->SendMode(parameters, 3, n); + } + delete n; +} + +void ModeChannelHalfOp::RemoveMode(userrec* user) +{ +} + +ModeAction ModeChannelHalfOp::OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string ¶meter, bool adding) +{ + /* If halfops are not enabled in the conf, we don't execute + * anything in this class at all. + */ + if (!ServerInstance->Config->AllowHalfop) + { + parameter = ""; + return MODEACTION_DENY; + } + + int status = channel->GetStatus(source); + + /* Call the correct method depending on wether we're adding or removing the mode */ + if (adding) + { + parameter = this->AddHalfOp(source, parameter.c_str(), channel, status); + } + else + { + parameter = this->DelHalfOp(source, parameter.c_str(), channel, status); + } + /* If the method above 'ate' the parameter by reducing it to an empty string, then + * it won't matter wether we return ALLOW or DENY here, as an empty string overrides + * the return value and is always MODEACTION_DENY if the mode is supposed to have + * a parameter. + */ + if (parameter.length()) + return MODEACTION_ALLOW; + else + return MODEACTION_DENY; +} + +std::string ModeChannelHalfOp::AddHalfOp(userrec *user,const char* dest,chanrec *chan,int status) +{ + userrec *d = ServerInstance->Modes->SanityChecks(user,dest,chan,status); + + if (d) + { + if (IS_LOCAL(user)) + { + int MOD_RESULT = 0; + FOREACH_RESULT(I_OnAccessCheck,OnAccessCheck(user,d,chan,AC_HALFOP)); + + if (MOD_RESULT == ACR_DENY) + return ""; + if (MOD_RESULT == ACR_DEFAULT) + { + if ((status < STATUS_OP) && (!ServerInstance->ULine(user->server))) + { + user->WriteServ("482 %s %s :You're not a channel operator",user->nick, chan->name); + return ""; + } + } + } + + return ServerInstance->Modes->Grant(d,chan,UCMODE_HOP); + } + return ""; +} + +std::string ModeChannelHalfOp::DelHalfOp(userrec *user,const char *dest,chanrec *chan,int status) +{ + userrec *d = ServerInstance->Modes->SanityChecks(user,dest,chan,status); + + if (d) + { + if (IS_LOCAL(user)) + { + int MOD_RESULT = 0; + FOREACH_RESULT(I_OnAccessCheck,OnAccessCheck(user,d,chan,AC_DEHALFOP)); + + if (MOD_RESULT == ACR_DENY) + return ""; + if (MOD_RESULT == ACR_DEFAULT) + { + if ((user != d) && ((status < STATUS_OP) && (!ServerInstance->ULine(user->server)))) + { + user->WriteServ("482 %s %s :You are not a channel operator",user->nick, chan->name); + return ""; + } + } + } + + return ServerInstance->Modes->Revoke(d,chan,UCMODE_HOP); + } + return ""; +} + diff --git a/src/modes/cmode_i.cpp b/src/modes/cmode_i.cpp index 1dd6dc857..12bdb18b8 100644 --- a/src/modes/cmode_i.cpp +++ b/src/modes/cmode_i.cpp @@ -1 +1,35 @@ -/* +------------------------------------+
* | 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 "mode.h"
#include "channels.h"
#include "users.h"
#include "modes/cmode_i.h"
ModeChannelInviteOnly::ModeChannelInviteOnly(InspIRCd* Instance) : ModeHandler(Instance, 'i', 0, 0, false, MODETYPE_CHANNEL, false)
{
}
ModeAction ModeChannelInviteOnly::OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string ¶meter, bool adding)
{
if (channel->modes[CM_INVITEONLY] != adding)
{
channel->modes[CM_INVITEONLY] = adding;
return MODEACTION_ALLOW;
}
else
{
return MODEACTION_DENY;
}
}
\ No newline at end of file +/* +------------------------------------+ + * | 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 "mode.h" +#include "channels.h" +#include "users.h" +#include "modes/cmode_i.h" + +ModeChannelInviteOnly::ModeChannelInviteOnly(InspIRCd* Instance) : ModeHandler(Instance, 'i', 0, 0, false, MODETYPE_CHANNEL, false) +{ +} + +ModeAction ModeChannelInviteOnly::OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string ¶meter, bool adding) +{ + if (channel->modes[CM_INVITEONLY] != adding) + { + channel->modes[CM_INVITEONLY] = adding; + return MODEACTION_ALLOW; + } + else + { + return MODEACTION_DENY; + } +} diff --git a/src/modes/cmode_k.cpp b/src/modes/cmode_k.cpp index e06c35e83..eb59714f7 100644 --- a/src/modes/cmode_k.cpp +++ b/src/modes/cmode_k.cpp @@ -1 +1,103 @@ -/* +------------------------------------+
* | 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 "mode.h"
#include "channels.h"
#include "users.h"
#include "modes/cmode_k.h"
ModeChannelKey::ModeChannelKey(InspIRCd* Instance) : ModeHandler(Instance, 'k', 1, 1, false, MODETYPE_CHANNEL, false)
{
}
ModePair ModeChannelKey::ModeSet(userrec* source, userrec* dest, chanrec* channel, const std::string ¶meter)
{
if (channel->modes[CM_KEY])
{
return std::make_pair(true, channel->key);
}
else
{
return std::make_pair(false, parameter);
}
}
void ModeChannelKey::RemoveMode(chanrec* channel)
{
/** +k needs a parameter when being removed,
* so we have a special-case RemoveMode here for it
*/
char moderemove[MAXBUF];
const char* parameters[] = { channel->name, moderemove, channel->key };
if (channel->IsModeSet(this->GetModeChar()))
{
userrec* n = new userrec(ServerInstance);
sprintf(moderemove,"-%c",this->GetModeChar());
n->SetFd(FD_MAGIC_NUMBER);
ServerInstance->SendMode(parameters, 3, n);
delete n;
}
}
void ModeChannelKey::RemoveMode(userrec* user)
{
}
bool ModeChannelKey::CheckTimeStamp(time_t theirs, time_t ours, const std::string &their_param, const std::string &our_param, chanrec* channel)
{
/* When TS is equal, the alphabetically later channel key wins */
return (their_param < our_param);
}
ModeAction ModeChannelKey::OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string ¶meter, bool adding)
{
if ((channel->modes[CM_KEY] != adding) || (!IS_LOCAL(source)))
{
if (((channel->modes[CM_KEY]) && (strcasecmp(parameter.c_str(),channel->key))) && (IS_LOCAL(source)))
{
/* Key is currently set and the correct key wasnt given */
return MODEACTION_DENY;
}
else if ((!channel->modes[CM_KEY]) || ((adding) && (!IS_LOCAL(source))))
{
/* Key isnt currently set */
if ((parameter.length()) && (parameter.rfind(' ') == std::string::npos))
{
strlcpy(channel->key,parameter.c_str(),32);
channel->modes[CM_KEY] = adding;
parameter = channel->key;
return MODEACTION_ALLOW;
}
else
return MODEACTION_DENY;
}
else if (((channel->modes[CM_KEY]) && (!strcasecmp(parameter.c_str(),channel->key))) || ((!adding) && (!IS_LOCAL(source))))
{
/* Key is currently set, and correct key was given */
*channel->key = 0;
channel->modes[CM_KEY] = adding;
return MODEACTION_ALLOW;
}
return MODEACTION_DENY;
}
else
{
return MODEACTION_DENY;
}
}
\ No newline at end of file +/* +------------------------------------+ + * | 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 "mode.h" +#include "channels.h" +#include "users.h" +#include "modes/cmode_k.h" + +ModeChannelKey::ModeChannelKey(InspIRCd* Instance) : ModeHandler(Instance, 'k', 1, 1, false, MODETYPE_CHANNEL, false) +{ +} + +ModePair ModeChannelKey::ModeSet(userrec* source, userrec* dest, chanrec* channel, const std::string ¶meter) +{ + if (channel->modes[CM_KEY]) + { + return std::make_pair(true, channel->key); + } + else + { + return std::make_pair(false, parameter); + } +} + +void ModeChannelKey::RemoveMode(chanrec* channel) +{ + /** +k needs a parameter when being removed, + * so we have a special-case RemoveMode here for it + */ + char moderemove[MAXBUF]; + const char* parameters[] = { channel->name, moderemove, channel->key }; + + if (channel->IsModeSet(this->GetModeChar())) + { + userrec* n = new userrec(ServerInstance); + + sprintf(moderemove,"-%c",this->GetModeChar()); + n->SetFd(FD_MAGIC_NUMBER); + + ServerInstance->SendMode(parameters, 3, n); + + delete n; + } +} + +void ModeChannelKey::RemoveMode(userrec* user) +{ +} + +bool ModeChannelKey::CheckTimeStamp(time_t theirs, time_t ours, const std::string &their_param, const std::string &our_param, chanrec* channel) +{ + /* When TS is equal, the alphabetically later channel key wins */ + return (their_param < our_param); +} + +ModeAction ModeChannelKey::OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string ¶meter, bool adding) +{ + if ((channel->modes[CM_KEY] != adding) || (!IS_LOCAL(source))) + { + if (((channel->modes[CM_KEY]) && (strcasecmp(parameter.c_str(),channel->key))) && (IS_LOCAL(source))) + { + /* Key is currently set and the correct key wasnt given */ + return MODEACTION_DENY; + } + else if ((!channel->modes[CM_KEY]) || ((adding) && (!IS_LOCAL(source)))) + { + /* Key isnt currently set */ + if ((parameter.length()) && (parameter.rfind(' ') == std::string::npos)) + { + strlcpy(channel->key,parameter.c_str(),32); + channel->modes[CM_KEY] = adding; + parameter = channel->key; + return MODEACTION_ALLOW; + } + else + return MODEACTION_DENY; + } + else if (((channel->modes[CM_KEY]) && (!strcasecmp(parameter.c_str(),channel->key))) || ((!adding) && (!IS_LOCAL(source)))) + { + /* Key is currently set, and correct key was given */ + *channel->key = 0; + channel->modes[CM_KEY] = adding; + return MODEACTION_ALLOW; + } + return MODEACTION_DENY; + } + else + { + return MODEACTION_DENY; + } +} + diff --git a/src/modes/cmode_l.cpp b/src/modes/cmode_l.cpp index 5e1a8c26c..1a57a440d 100644 --- a/src/modes/cmode_l.cpp +++ b/src/modes/cmode_l.cpp @@ -1 +1,97 @@ -/* +------------------------------------+
* | 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 "mode.h"
#include "channels.h"
#include "users.h"
#include "modes/cmode_l.h"
ModeChannelLimit::ModeChannelLimit(InspIRCd* Instance) : ModeHandler(Instance, 'l', 1, 0, false, MODETYPE_CHANNEL, false)
{
}
ModePair ModeChannelLimit::ModeSet(userrec* source, userrec* dest, chanrec* channel, const std::string ¶meter)
{
if (channel->limit)
{
return std::make_pair(true, ConvToStr(channel->limit));
}
else
{
return std::make_pair(false, parameter);
}
}
bool ModeChannelLimit::CheckTimeStamp(time_t theirs, time_t ours, const std::string &their_param, const std::string &our_param, chanrec* channel)
{
/* When TS is equal, the higher channel limit wins */
return (atoi(their_param.c_str()) < atoi(our_param.c_str()));
}
ModeAction ModeChannelLimit::OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string ¶meter, bool adding)
{
if (adding)
{
/* Setting a new limit, sanity check */
long limit = atoi(parameter.c_str());
/* Wrap low values at 32768 */
if (limit < 0)
limit = 0x7FFF;
/* If the new limit is the same as the old limit,
* and the old limit isnt 0, disallow */
if ((limit == channel->limit) && (channel->limit > 0))
{
parameter = "";
return MODEACTION_DENY;
}
/* They must have specified an invalid number.
* Dont allow +l 0.
*/
if (!limit)
{
parameter = "";
return MODEACTION_DENY;
}
parameter = ConvToStr(limit);
/* Set new limit */
channel->limit = limit;
channel->modes[CM_LIMIT] = 1;
return MODEACTION_ALLOW;
}
else
{
/* Check if theres a limit here to remove.
* If there isnt, dont allow the -l
*/
if (!channel->limit)
{
parameter = "";
return MODEACTION_DENY;
}
/* Removing old limit, no checks here */
channel->limit = 0;
channel->modes[CM_LIMIT] = 0;
return MODEACTION_ALLOW;
}
return MODEACTION_DENY;
}
\ No newline at end of file +/* +------------------------------------+ + * | 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 "mode.h" +#include "channels.h" +#include "users.h" +#include "modes/cmode_l.h" + +ModeChannelLimit::ModeChannelLimit(InspIRCd* Instance) : ModeHandler(Instance, 'l', 1, 0, false, MODETYPE_CHANNEL, false) +{ +} + +ModePair ModeChannelLimit::ModeSet(userrec* source, userrec* dest, chanrec* channel, const std::string ¶meter) +{ + if (channel->limit) + { + return std::make_pair(true, ConvToStr(channel->limit)); + } + else + { + return std::make_pair(false, parameter); + } +} + +bool ModeChannelLimit::CheckTimeStamp(time_t theirs, time_t ours, const std::string &their_param, const std::string &our_param, chanrec* channel) +{ + /* When TS is equal, the higher channel limit wins */ + return (atoi(their_param.c_str()) < atoi(our_param.c_str())); +} + +ModeAction ModeChannelLimit::OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string ¶meter, bool adding) +{ + if (adding) + { + /* Setting a new limit, sanity check */ + long limit = atoi(parameter.c_str()); + + /* Wrap low values at 32768 */ + if (limit < 0) + limit = 0x7FFF; + + /* If the new limit is the same as the old limit, + * and the old limit isnt 0, disallow */ + if ((limit == channel->limit) && (channel->limit > 0)) + { + parameter = ""; + return MODEACTION_DENY; + } + + /* They must have specified an invalid number. + * Dont allow +l 0. + */ + if (!limit) + { + parameter = ""; + return MODEACTION_DENY; + } + + parameter = ConvToStr(limit); + + /* Set new limit */ + channel->limit = limit; + channel->modes[CM_LIMIT] = 1; + + return MODEACTION_ALLOW; + } + else + { + /* Check if theres a limit here to remove. + * If there isnt, dont allow the -l + */ + if (!channel->limit) + { + parameter = ""; + return MODEACTION_DENY; + } + + /* Removing old limit, no checks here */ + channel->limit = 0; + channel->modes[CM_LIMIT] = 0; + + return MODEACTION_ALLOW; + } + + return MODEACTION_DENY; +} diff --git a/src/modes/cmode_m.cpp b/src/modes/cmode_m.cpp index da882333b..520248fda 100644 --- a/src/modes/cmode_m.cpp +++ b/src/modes/cmode_m.cpp @@ -1 +1,36 @@ -/* +------------------------------------+
* | 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 "mode.h"
#include "channels.h"
#include "users.h"
#include "modes/cmode_m.h"
ModeChannelModerated::ModeChannelModerated(InspIRCd* Instance) : ModeHandler(Instance, 'm', 0, 0, false, MODETYPE_CHANNEL, false)
{
}
ModeAction ModeChannelModerated::OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string ¶meter, bool adding)
{
if (channel->modes[CM_MODERATED] != adding)
{
channel->modes[CM_MODERATED] = adding;
return MODEACTION_ALLOW;
}
else
{
return MODEACTION_DENY;
}
}
\ No newline at end of file +/* +------------------------------------+ + * | 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 "mode.h" +#include "channels.h" +#include "users.h" +#include "modes/cmode_m.h" + +ModeChannelModerated::ModeChannelModerated(InspIRCd* Instance) : ModeHandler(Instance, 'm', 0, 0, false, MODETYPE_CHANNEL, false) +{ +} + +ModeAction ModeChannelModerated::OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string ¶meter, bool adding) +{ + if (channel->modes[CM_MODERATED] != adding) + { + channel->modes[CM_MODERATED] = adding; + return MODEACTION_ALLOW; + } + else + { + return MODEACTION_DENY; + } +} + diff --git a/src/modes/cmode_n.cpp b/src/modes/cmode_n.cpp index 3ae7d538c..ddc2e1bbd 100644 --- a/src/modes/cmode_n.cpp +++ b/src/modes/cmode_n.cpp @@ -1 +1,36 @@ -/* +------------------------------------+
* | 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 "mode.h"
#include "channels.h"
#include "users.h"
#include "modes/cmode_n.h"
ModeChannelNoExternal::ModeChannelNoExternal(InspIRCd* Instance) : ModeHandler(Instance, 'n', 0, 0, false, MODETYPE_CHANNEL, false)
{
}
ModeAction ModeChannelNoExternal::OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string ¶meter, bool adding)
{
if (channel->modes[CM_NOEXTERNAL] != adding)
{
channel->modes[CM_NOEXTERNAL] = adding;
return MODEACTION_ALLOW;
}
else
{
return MODEACTION_DENY;
}
}
\ No newline at end of file +/* +------------------------------------+ + * | 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 "mode.h" +#include "channels.h" +#include "users.h" +#include "modes/cmode_n.h" + +ModeChannelNoExternal::ModeChannelNoExternal(InspIRCd* Instance) : ModeHandler(Instance, 'n', 0, 0, false, MODETYPE_CHANNEL, false) +{ +} + +ModeAction ModeChannelNoExternal::OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string ¶meter, bool adding) +{ + if (channel->modes[CM_NOEXTERNAL] != adding) + { + channel->modes[CM_NOEXTERNAL] = adding; + return MODEACTION_ALLOW; + } + else + { + return MODEACTION_DENY; + } +} + diff --git a/src/modes/cmode_o.cpp b/src/modes/cmode_o.cpp index 9ae18109e..47d191ff8 100644 --- a/src/modes/cmode_o.cpp +++ b/src/modes/cmode_o.cpp @@ -1 +1,153 @@ -/* +------------------------------------+
* | 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 "mode.h"
#include "channels.h"
#include "users.h"
#include "modules.h"
#include "modes/cmode_o.h"
ModeChannelOp::ModeChannelOp(InspIRCd* Instance) : ModeHandler(Instance, 'o', 1, 1, true, MODETYPE_CHANNEL, false, '@')
{
}
unsigned int ModeChannelOp::GetPrefixRank()
{
return OP_VALUE;
}
ModePair ModeChannelOp::ModeSet(userrec* source, userrec* dest, chanrec* channel, const std::string ¶meter)
{
userrec* x = ServerInstance->FindNick(parameter);
if (x)
{
if (channel->GetStatusFlags(x) & UCMODE_OP)
{
return std::make_pair(true, x->nick);
}
else
{
return std::make_pair(false, parameter);
}
}
return std::make_pair(false, parameter);
}
void ModeChannelOp::RemoveMode(chanrec* channel)
{
CUList* list = channel->GetOppedUsers();
CUList copy;
char moderemove[MAXBUF];
userrec* n = new userrec(ServerInstance);
n->SetFd(FD_MAGIC_NUMBER);
for (CUList::iterator i = list->begin(); i != list->end(); i++)
{
userrec* n = i->first;
copy.insert(std::make_pair(n,n->nick));
}
for (CUList::iterator i = copy.begin(); i != copy.end(); i++)
{
sprintf(moderemove,"-%c",this->GetModeChar());
const char* parameters[] = { channel->name, moderemove, i->first->nick };
ServerInstance->SendMode(parameters, 3, n);
}
delete n;
}
void ModeChannelOp::RemoveMode(userrec* user)
{
}
ModeAction ModeChannelOp::OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string ¶meter, bool adding)
{
int status = channel->GetStatus(source);
/* Call the correct method depending on wether we're adding or removing the mode */
if (adding)
{
parameter = this->AddOp(source, parameter.c_str(), channel, status);
}
else
{
parameter = this->DelOp(source, parameter.c_str(), channel, status);
}
/* If the method above 'ate' the parameter by reducing it to an empty string, then
* it won't matter wether we return ALLOW or DENY here, as an empty string overrides
* the return value and is always MODEACTION_DENY if the mode is supposed to have
* a parameter.
*/
if (parameter.length())
return MODEACTION_ALLOW;
else
return MODEACTION_DENY;
}
std::string ModeChannelOp::AddOp(userrec *user,const char* dest,chanrec *chan,int status)
{
userrec *d = ServerInstance->Modes->SanityChecks(user,dest,chan,status);
if (d)
{
if (IS_LOCAL(user))
{
int MOD_RESULT = 0;
FOREACH_RESULT(I_OnAccessCheck,OnAccessCheck(user,d,chan,AC_OP));
if (MOD_RESULT == ACR_DENY)
return "";
if (MOD_RESULT == ACR_DEFAULT)
{
if ((status < STATUS_OP) && (!ServerInstance->ULine(user->server)))
{
user->WriteServ("482 %s %s :You're not a channel operator",user->nick, chan->name);
return "";
}
}
}
return ServerInstance->Modes->Grant(d,chan,UCMODE_OP);
}
return "";
}
std::string ModeChannelOp::DelOp(userrec *user,const char *dest,chanrec *chan,int status)
{
userrec *d = ServerInstance->Modes->SanityChecks(user,dest,chan,status);
if (d)
{
if (IS_LOCAL(user))
{
int MOD_RESULT = 0;
FOREACH_RESULT(I_OnAccessCheck,OnAccessCheck(user,d,chan,AC_DEOP));
if (MOD_RESULT == ACR_DENY)
return "";
if (MOD_RESULT == ACR_DEFAULT)
{
if ((status < STATUS_OP) && (!ServerInstance->ULine(user->server)) && (IS_LOCAL(user)))
{
user->WriteServ("482 %s %s :You are not a channel operator",user->nick, chan->name);
return "";
}
}
}
return ServerInstance->Modes->Revoke(d,chan,UCMODE_OP);
}
return "";
}
\ No newline at end of file +/* +------------------------------------+ + * | 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 "mode.h" +#include "channels.h" +#include "users.h" +#include "modules.h" +#include "modes/cmode_o.h" + +ModeChannelOp::ModeChannelOp(InspIRCd* Instance) : ModeHandler(Instance, 'o', 1, 1, true, MODETYPE_CHANNEL, false, '@') +{ +} + +unsigned int ModeChannelOp::GetPrefixRank() +{ + return OP_VALUE; +} + +ModePair ModeChannelOp::ModeSet(userrec* source, userrec* dest, chanrec* channel, const std::string ¶meter) +{ + userrec* x = ServerInstance->FindNick(parameter); + if (x) + { + if (channel->GetStatusFlags(x) & UCMODE_OP) + { + return std::make_pair(true, x->nick); + } + else + { + return std::make_pair(false, parameter); + } + } + return std::make_pair(false, parameter); +} + + +void ModeChannelOp::RemoveMode(chanrec* channel) +{ + CUList* list = channel->GetOppedUsers(); + CUList copy; + char moderemove[MAXBUF]; + userrec* n = new userrec(ServerInstance); + n->SetFd(FD_MAGIC_NUMBER); + + for (CUList::iterator i = list->begin(); i != list->end(); i++) + { + userrec* n = i->first; + copy.insert(std::make_pair(n,n->nick)); + } + for (CUList::iterator i = copy.begin(); i != copy.end(); i++) + { + sprintf(moderemove,"-%c",this->GetModeChar()); + const char* parameters[] = { channel->name, moderemove, i->first->nick }; + ServerInstance->SendMode(parameters, 3, n); + } + delete n; +} + +void ModeChannelOp::RemoveMode(userrec* user) +{ +} + +ModeAction ModeChannelOp::OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string ¶meter, bool adding) +{ + int status = channel->GetStatus(source); + + /* Call the correct method depending on wether we're adding or removing the mode */ + if (adding) + { + parameter = this->AddOp(source, parameter.c_str(), channel, status); + } + else + { + parameter = this->DelOp(source, parameter.c_str(), channel, status); + } + /* If the method above 'ate' the parameter by reducing it to an empty string, then + * it won't matter wether we return ALLOW or DENY here, as an empty string overrides + * the return value and is always MODEACTION_DENY if the mode is supposed to have + * a parameter. + */ + if (parameter.length()) + return MODEACTION_ALLOW; + else + return MODEACTION_DENY; +} + +std::string ModeChannelOp::AddOp(userrec *user,const char* dest,chanrec *chan,int status) +{ + userrec *d = ServerInstance->Modes->SanityChecks(user,dest,chan,status); + + if (d) + { + if (IS_LOCAL(user)) + { + int MOD_RESULT = 0; + FOREACH_RESULT(I_OnAccessCheck,OnAccessCheck(user,d,chan,AC_OP)); + + if (MOD_RESULT == ACR_DENY) + return ""; + if (MOD_RESULT == ACR_DEFAULT) + { + if ((status < STATUS_OP) && (!ServerInstance->ULine(user->server))) + { + user->WriteServ("482 %s %s :You're not a channel operator",user->nick, chan->name); + return ""; + } + } + } + + return ServerInstance->Modes->Grant(d,chan,UCMODE_OP); + } + return ""; +} + +std::string ModeChannelOp::DelOp(userrec *user,const char *dest,chanrec *chan,int status) +{ + userrec *d = ServerInstance->Modes->SanityChecks(user,dest,chan,status); + + if (d) + { + if (IS_LOCAL(user)) + { + int MOD_RESULT = 0; + FOREACH_RESULT(I_OnAccessCheck,OnAccessCheck(user,d,chan,AC_DEOP)); + + if (MOD_RESULT == ACR_DENY) + return ""; + if (MOD_RESULT == ACR_DEFAULT) + { + if ((status < STATUS_OP) && (!ServerInstance->ULine(user->server)) && (IS_LOCAL(user))) + { + user->WriteServ("482 %s %s :You are not a channel operator",user->nick, chan->name); + return ""; + } + } + } + + return ServerInstance->Modes->Revoke(d,chan,UCMODE_OP); + } + return ""; +} diff --git a/src/modes/cmode_p.cpp b/src/modes/cmode_p.cpp index c762b7827..15c33222e 100644 --- a/src/modes/cmode_p.cpp +++ b/src/modes/cmode_p.cpp @@ -1 +1,35 @@ -/* +------------------------------------+
* | 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 "mode.h"
#include "channels.h"
#include "users.h"
#include "modes/cmode_p.h"
ModeChannelPrivate::ModeChannelPrivate(InspIRCd* Instance) : ModeHandler(Instance, 'p', 0, 0, false, MODETYPE_CHANNEL, false)
{
}
ModeAction ModeChannelPrivate::OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string ¶meter, bool adding)
{
if (channel->modes[CM_PRIVATE] != adding)
{
channel->modes[CM_PRIVATE] = adding;
return MODEACTION_ALLOW;
}
else
{
return MODEACTION_DENY;
}
}
\ No newline at end of file +/* +------------------------------------+ + * | 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 "mode.h" +#include "channels.h" +#include "users.h" +#include "modes/cmode_p.h" + +ModeChannelPrivate::ModeChannelPrivate(InspIRCd* Instance) : ModeHandler(Instance, 'p', 0, 0, false, MODETYPE_CHANNEL, false) +{ +} + +ModeAction ModeChannelPrivate::OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string ¶meter, bool adding) +{ + if (channel->modes[CM_PRIVATE] != adding) + { + channel->modes[CM_PRIVATE] = adding; + return MODEACTION_ALLOW; + } + else + { + return MODEACTION_DENY; + } +} diff --git a/src/modes/cmode_s.cpp b/src/modes/cmode_s.cpp index 069591e6b..135291592 100644 --- a/src/modes/cmode_s.cpp +++ b/src/modes/cmode_s.cpp @@ -1 +1,35 @@ -/* +------------------------------------+
* | 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 "mode.h"
#include "channels.h"
#include "users.h"
#include "modes/cmode_s.h"
ModeChannelSecret::ModeChannelSecret(InspIRCd* Instance) : ModeHandler(Instance, 's', 0, 0, false, MODETYPE_CHANNEL, false)
{
}
ModeAction ModeChannelSecret::OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string ¶meter, bool adding)
{
if (channel->modes[CM_SECRET] != adding)
{
channel->modes[CM_SECRET] = adding;
return MODEACTION_ALLOW;
}
else
{
return MODEACTION_DENY;
}
}
\ No newline at end of file +/* +------------------------------------+ + * | 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 "mode.h" +#include "channels.h" +#include "users.h" +#include "modes/cmode_s.h" + +ModeChannelSecret::ModeChannelSecret(InspIRCd* Instance) : ModeHandler(Instance, 's', 0, 0, false, MODETYPE_CHANNEL, false) +{ +} + +ModeAction ModeChannelSecret::OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string ¶meter, bool adding) +{ + if (channel->modes[CM_SECRET] != adding) + { + channel->modes[CM_SECRET] = adding; + return MODEACTION_ALLOW; + } + else + { + return MODEACTION_DENY; + } +} diff --git a/src/modes/cmode_t.cpp b/src/modes/cmode_t.cpp index def48a781..2a6c06b42 100644 --- a/src/modes/cmode_t.cpp +++ b/src/modes/cmode_t.cpp @@ -1 +1,36 @@ -/* +------------------------------------+
* | 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 "mode.h"
#include "channels.h"
#include "users.h"
#include "modes/cmode_t.h"
ModeChannelTopicOps::ModeChannelTopicOps(InspIRCd* Instance) : ModeHandler(Instance, 't', 0, 0, false, MODETYPE_CHANNEL, false)
{
}
ModeAction ModeChannelTopicOps::OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string ¶meter, bool adding)
{
if (channel->modes[CM_TOPICLOCK] != adding)
{
channel->modes[CM_TOPICLOCK] = adding;
return MODEACTION_ALLOW;
}
else
{
return MODEACTION_DENY;
}
}
\ No newline at end of file +/* +------------------------------------+ + * | 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 "mode.h" +#include "channels.h" +#include "users.h" +#include "modes/cmode_t.h" + +ModeChannelTopicOps::ModeChannelTopicOps(InspIRCd* Instance) : ModeHandler(Instance, 't', 0, 0, false, MODETYPE_CHANNEL, false) +{ +} + +ModeAction ModeChannelTopicOps::OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string ¶meter, bool adding) +{ + if (channel->modes[CM_TOPICLOCK] != adding) + { + channel->modes[CM_TOPICLOCK] = adding; + return MODEACTION_ALLOW; + } + else + { + return MODEACTION_DENY; + } +} + diff --git a/src/modes/cmode_v.cpp b/src/modes/cmode_v.cpp index c055cca7f..1e244c606 100644 --- a/src/modes/cmode_v.cpp +++ b/src/modes/cmode_v.cpp @@ -1 +1,152 @@ -/* +------------------------------------+
* | 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 "mode.h"
#include "channels.h"
#include "users.h"
#include "modules.h"
#include "modes/cmode_v.h"
ModeChannelVoice::ModeChannelVoice(InspIRCd* Instance) : ModeHandler(Instance, 'v', 1, 1, true, MODETYPE_CHANNEL, false, '+')
{
}
unsigned int ModeChannelVoice::GetPrefixRank()
{
return VOICE_VALUE;
}
ModePair ModeChannelVoice::ModeSet(userrec* source, userrec* dest, chanrec* channel, const std::string ¶meter)
{
userrec* x = ServerInstance->FindNick(parameter);
if (x)
{
if (channel->GetStatusFlags(x) & UCMODE_VOICE)
{
return std::make_pair(true, x->nick);
}
else
{
return std::make_pair(false, parameter);
}
}
return std::make_pair(false, parameter);
}
void ModeChannelVoice::RemoveMode(chanrec* channel)
{
CUList* list = channel->GetVoicedUsers();
CUList copy;
char moderemove[MAXBUF];
userrec* n = new userrec(ServerInstance);
n->SetFd(FD_MAGIC_NUMBER);
for (CUList::iterator i = list->begin(); i != list->end(); i++)
{
userrec* n = i->first;
copy.insert(std::make_pair(n,n->nick));
}
for (CUList::iterator i = copy.begin(); i != copy.end(); i++)
{
sprintf(moderemove,"-%c",this->GetModeChar());
const char* parameters[] = { channel->name, moderemove, i->first->nick };
ServerInstance->SendMode(parameters, 3, n);
}
delete n;
}
void ModeChannelVoice::RemoveMode(userrec* user)
{
}
ModeAction ModeChannelVoice::OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string ¶meter, bool adding)
{
int status = channel->GetStatus(source);
/* Call the correct method depending on wether we're adding or removing the mode */
if (adding)
{
parameter = this->AddVoice(source, parameter.c_str(), channel, status);
}
else
{
parameter = this->DelVoice(source, parameter.c_str(), channel, status);
}
/* If the method above 'ate' the parameter by reducing it to an empty string, then
* it won't matter wether we return ALLOW or DENY here, as an empty string overrides
* the return value and is always MODEACTION_DENY if the mode is supposed to have
* a parameter.
*/
if (parameter.length())
return MODEACTION_ALLOW;
else
return MODEACTION_DENY;
}
std::string ModeChannelVoice::AddVoice(userrec *user,const char* dest,chanrec *chan,int status)
{
userrec *d = ServerInstance->Modes->SanityChecks(user,dest,chan,status);
if (d)
{
if (IS_LOCAL(user))
{
int MOD_RESULT = 0;
FOREACH_RESULT(I_OnAccessCheck,OnAccessCheck(user,d,chan,AC_VOICE));
if (MOD_RESULT == ACR_DENY)
return "";
if (MOD_RESULT == ACR_DEFAULT)
{
if ((status < STATUS_HOP) && (!ServerInstance->ULine(user->server)))
{
user->WriteServ("482 %s %s :You're not a channel (half)operator",user->nick, chan->name);
return "";
}
}
}
return ServerInstance->Modes->Grant(d,chan,UCMODE_VOICE);
}
return "";
}
std::string ModeChannelVoice::DelVoice(userrec *user,const char *dest,chanrec *chan,int status)
{
userrec *d = ServerInstance->Modes->SanityChecks(user,dest,chan,status);
if (d)
{
if (IS_LOCAL(user))
{
int MOD_RESULT = 0;
FOREACH_RESULT(I_OnAccessCheck,OnAccessCheck(user,d,chan,AC_DEVOICE));
if (MOD_RESULT == ACR_DENY)
return "";
if (MOD_RESULT == ACR_DEFAULT)
{
if ((status < STATUS_HOP) && (!ServerInstance->ULine(user->server)))
{
user->WriteServ("482 %s %s :You are not a channel (half)operator",user->nick, chan->name);
return "";
}
}
}
return ServerInstance->Modes->Revoke(d,chan,UCMODE_VOICE);
}
return "";
}
\ No newline at end of file +/* +------------------------------------+ + * | 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 "mode.h" +#include "channels.h" +#include "users.h" +#include "modules.h" +#include "modes/cmode_v.h" + +ModeChannelVoice::ModeChannelVoice(InspIRCd* Instance) : ModeHandler(Instance, 'v', 1, 1, true, MODETYPE_CHANNEL, false, '+') +{ +} + +unsigned int ModeChannelVoice::GetPrefixRank() +{ + return VOICE_VALUE; +} + +ModePair ModeChannelVoice::ModeSet(userrec* source, userrec* dest, chanrec* channel, const std::string ¶meter) +{ + userrec* x = ServerInstance->FindNick(parameter); + if (x) + { + if (channel->GetStatusFlags(x) & UCMODE_VOICE) + { + return std::make_pair(true, x->nick); + } + else + { + return std::make_pair(false, parameter); + } + } + return std::make_pair(false, parameter); +} + +void ModeChannelVoice::RemoveMode(chanrec* channel) +{ + CUList* list = channel->GetVoicedUsers(); + CUList copy; + char moderemove[MAXBUF]; + userrec* n = new userrec(ServerInstance); + n->SetFd(FD_MAGIC_NUMBER); + + for (CUList::iterator i = list->begin(); i != list->end(); i++) + { + userrec* n = i->first; + copy.insert(std::make_pair(n,n->nick)); + } + for (CUList::iterator i = copy.begin(); i != copy.end(); i++) + { + sprintf(moderemove,"-%c",this->GetModeChar()); + const char* parameters[] = { channel->name, moderemove, i->first->nick }; + ServerInstance->SendMode(parameters, 3, n); + } + delete n; +} + +void ModeChannelVoice::RemoveMode(userrec* user) +{ +} + +ModeAction ModeChannelVoice::OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string ¶meter, bool adding) +{ + int status = channel->GetStatus(source); + + /* Call the correct method depending on wether we're adding or removing the mode */ + if (adding) + { + parameter = this->AddVoice(source, parameter.c_str(), channel, status); + } + else + { + parameter = this->DelVoice(source, parameter.c_str(), channel, status); + } + /* If the method above 'ate' the parameter by reducing it to an empty string, then + * it won't matter wether we return ALLOW or DENY here, as an empty string overrides + * the return value and is always MODEACTION_DENY if the mode is supposed to have + * a parameter. + */ + if (parameter.length()) + return MODEACTION_ALLOW; + else + return MODEACTION_DENY; +} + +std::string ModeChannelVoice::AddVoice(userrec *user,const char* dest,chanrec *chan,int status) +{ + userrec *d = ServerInstance->Modes->SanityChecks(user,dest,chan,status); + + if (d) + { + if (IS_LOCAL(user)) + { + int MOD_RESULT = 0; + FOREACH_RESULT(I_OnAccessCheck,OnAccessCheck(user,d,chan,AC_VOICE)); + + if (MOD_RESULT == ACR_DENY) + return ""; + if (MOD_RESULT == ACR_DEFAULT) + { + if ((status < STATUS_HOP) && (!ServerInstance->ULine(user->server))) + { + user->WriteServ("482 %s %s :You're not a channel (half)operator",user->nick, chan->name); + return ""; + } + } + } + + return ServerInstance->Modes->Grant(d,chan,UCMODE_VOICE); + } + return ""; +} + +std::string ModeChannelVoice::DelVoice(userrec *user,const char *dest,chanrec *chan,int status) +{ + userrec *d = ServerInstance->Modes->SanityChecks(user,dest,chan,status); + + if (d) + { + if (IS_LOCAL(user)) + { + int MOD_RESULT = 0; + FOREACH_RESULT(I_OnAccessCheck,OnAccessCheck(user,d,chan,AC_DEVOICE)); + + if (MOD_RESULT == ACR_DENY) + return ""; + if (MOD_RESULT == ACR_DEFAULT) + { + if ((status < STATUS_HOP) && (!ServerInstance->ULine(user->server))) + { + user->WriteServ("482 %s %s :You are not a channel (half)operator",user->nick, chan->name); + return ""; + } + } + } + + return ServerInstance->Modes->Revoke(d,chan,UCMODE_VOICE); + } + return ""; +} diff --git a/src/modes/umode_i.cpp b/src/modes/umode_i.cpp index 6bd769a9a..5a9327375 100644 --- a/src/modes/umode_i.cpp +++ b/src/modes/umode_i.cpp @@ -1 +1,45 @@ -/* +------------------------------------+
* | 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 "mode.h"
#include "channels.h"
#include "users.h"
#include "modes/umode_i.h"
ModeUserInvisible::ModeUserInvisible(InspIRCd* Instance) : ModeHandler(Instance, 'i', 0, 0, false, MODETYPE_USER, false)
{
}
ModeAction ModeUserInvisible::OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string ¶meter, bool adding)
{
/* Only opers can change other users modes */
if ((source != dest) && (!*source->oper))
return MODEACTION_DENY;
/* Set the bitfields */
if (dest->modes[UM_INVISIBLE] != adding)
{
dest->modes[UM_INVISIBLE] = adding;
this->count += (adding ? 1: -1);
return MODEACTION_ALLOW;
}
/* Allow the change */
return MODEACTION_DENY;
}
unsigned int ModeUserInvisible::GetCount()
{
return count;
}
\ No newline at end of file +/* +------------------------------------+ + * | 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 "mode.h" +#include "channels.h" +#include "users.h" +#include "modes/umode_i.h" + +ModeUserInvisible::ModeUserInvisible(InspIRCd* Instance) : ModeHandler(Instance, 'i', 0, 0, false, MODETYPE_USER, false) +{ +} + +ModeAction ModeUserInvisible::OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string ¶meter, bool adding) +{ + /* Only opers can change other users modes */ + if ((source != dest) && (!*source->oper)) + return MODEACTION_DENY; + + /* Set the bitfields */ + if (dest->modes[UM_INVISIBLE] != adding) + { + dest->modes[UM_INVISIBLE] = adding; + this->count += (adding ? 1: -1); + return MODEACTION_ALLOW; + } + + /* Allow the change */ + return MODEACTION_DENY; +} + +unsigned int ModeUserInvisible::GetCount() +{ + return count; +} diff --git a/src/modes/umode_n.cpp b/src/modes/umode_n.cpp index c5d2599d8..c9c9e312e 100644 --- a/src/modes/umode_n.cpp +++ b/src/modes/umode_n.cpp @@ -1 +1,58 @@ -/* +------------------------------------+
* | 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 "mode.h"
#include "channels.h"
#include "users.h"
#include "modes/umode_n.h"
ModeUserServerNoticeMask::ModeUserServerNoticeMask(InspIRCd* Instance) : ModeHandler(Instance, 'n', 1, 0, false, MODETYPE_USER, true)
{
}
ModeAction ModeUserServerNoticeMask::OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string ¶meter, bool adding)
{
/* Only opers can change other users modes */
if ((source != dest) && (!*source->oper))
return MODEACTION_DENY;
/* Set the bitfields */
if (adding)
{
/* Fix for bug #310 reported by Smartys */
if (!dest->modes[UM_SNOMASK])
memset(dest->snomasks, 0, sizeof(dest->snomasks));
parameter = dest->ProcessNoticeMasks(parameter.c_str());
dest->modes[UM_SNOMASK] = true;
if (!dest->modes[UM_SERVERNOTICE])
{
const char* newmodes[] = { dest->nick, "+s" };
ServerInstance->Modes->Process(newmodes, 2, source, true);
}
return MODEACTION_ALLOW;
}
else
{
if (dest->modes[UM_SNOMASK] != false)
{
dest->modes[UM_SNOMASK] = false;
return MODEACTION_ALLOW;
}
}
/* Allow the change */
return MODEACTION_DENY;
}
\ No newline at end of file +/* +------------------------------------+ + * | 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 "mode.h" +#include "channels.h" +#include "users.h" +#include "modes/umode_n.h" + +ModeUserServerNoticeMask::ModeUserServerNoticeMask(InspIRCd* Instance) : ModeHandler(Instance, 'n', 1, 0, false, MODETYPE_USER, true) +{ +} + +ModeAction ModeUserServerNoticeMask::OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string ¶meter, bool adding) +{ + /* Only opers can change other users modes */ + if ((source != dest) && (!*source->oper)) + return MODEACTION_DENY; + + /* Set the bitfields */ + if (adding) + { + /* Fix for bug #310 reported by Smartys */ + if (!dest->modes[UM_SNOMASK]) + memset(dest->snomasks, 0, sizeof(dest->snomasks)); + + parameter = dest->ProcessNoticeMasks(parameter.c_str()); + dest->modes[UM_SNOMASK] = true; + if (!dest->modes[UM_SERVERNOTICE]) + { + const char* newmodes[] = { dest->nick, "+s" }; + ServerInstance->Modes->Process(newmodes, 2, source, true); + } + return MODEACTION_ALLOW; + } + else + { + if (dest->modes[UM_SNOMASK] != false) + { + dest->modes[UM_SNOMASK] = false; + return MODEACTION_ALLOW; + } + } + + /* Allow the change */ + return MODEACTION_DENY; +} + diff --git a/src/modes/umode_o.cpp b/src/modes/umode_o.cpp index 1b47fe8b2..30ed089f6 100644 --- a/src/modes/umode_o.cpp +++ b/src/modes/umode_o.cpp @@ -1 +1,49 @@ -/* +------------------------------------+
* | 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 "mode.h"
#include "channels.h"
#include "users.h"
#include "modes/umode_o.h"
ModeUserOperator::ModeUserOperator(InspIRCd* Instance) : ModeHandler(Instance, 'o', 0, 0, false, MODETYPE_USER, true)
{
}
ModeAction ModeUserOperator::OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string ¶meter, bool adding)
{
/* Only opers can execute this class at all */
if (!*source->oper)
return MODEACTION_DENY;
/* Not even opers can GIVE the +o mode, only take it away */
if (adding)
return MODEACTION_DENY;
/* Set the bitfields.
* Note that oper status is only given in cmd_oper.cpp
* NOT here. It is impossible to directly set +o without
* verifying as an oper and getting an opertype assigned
* to your userrec!
*/
ServerInstance->SNO->WriteToSnoMask('o', "User %s de-opered (by %s)", dest->nick, source->nick);
dest->UnOper();
return MODEACTION_ALLOW;
}
unsigned int ModeUserOperator::GetCount()
{
return ServerInstance->all_opers.size();
}
\ No newline at end of file +/* +------------------------------------+ + * | 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 "mode.h" +#include "channels.h" +#include "users.h" +#include "modes/umode_o.h" + +ModeUserOperator::ModeUserOperator(InspIRCd* Instance) : ModeHandler(Instance, 'o', 0, 0, false, MODETYPE_USER, true) +{ +} + +ModeAction ModeUserOperator::OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string ¶meter, bool adding) +{ + /* Only opers can execute this class at all */ + if (!*source->oper) + return MODEACTION_DENY; + + /* Not even opers can GIVE the +o mode, only take it away */ + if (adding) + return MODEACTION_DENY; + + /* Set the bitfields. + * Note that oper status is only given in cmd_oper.cpp + * NOT here. It is impossible to directly set +o without + * verifying as an oper and getting an opertype assigned + * to your userrec! + */ + ServerInstance->SNO->WriteToSnoMask('o', "User %s de-opered (by %s)", dest->nick, source->nick); + dest->UnOper(); + + return MODEACTION_ALLOW; +} + +unsigned int ModeUserOperator::GetCount() +{ + return ServerInstance->all_opers.size(); +} diff --git a/src/modes/umode_s.cpp b/src/modes/umode_s.cpp index f0441b85f..4b3179001 100644 --- a/src/modes/umode_s.cpp +++ b/src/modes/umode_s.cpp @@ -1 +1,45 @@ -/* +------------------------------------+
* | 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 "mode.h"
#include "channels.h"
#include "users.h"
#include "modes/umode_s.h"
ModeUserServerNotice::ModeUserServerNotice(InspIRCd* Instance) : ModeHandler(Instance, 's', 0, 0, false, MODETYPE_USER, false)
{
}
ModeAction ModeUserServerNotice::OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string ¶meter, bool adding)
{
/* Only opers can change other users modes */
if ((source != dest) && (!*source->oper))
return MODEACTION_DENY;
/* Set the bitfields */
if (dest->modes[UM_SERVERNOTICE] != adding)
{
dest->modes[UM_SERVERNOTICE] = adding;
this->count += (adding ? 1: -1);
return MODEACTION_ALLOW;
}
/* Allow the change */
return MODEACTION_DENY;
}
unsigned int ModeUserServerNotice::GetCount()
{
return count;
}
\ No newline at end of file +/* +------------------------------------+ + * | 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 "mode.h" +#include "channels.h" +#include "users.h" +#include "modes/umode_s.h" + +ModeUserServerNotice::ModeUserServerNotice(InspIRCd* Instance) : ModeHandler(Instance, 's', 0, 0, false, MODETYPE_USER, false) +{ +} + +ModeAction ModeUserServerNotice::OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string ¶meter, bool adding) +{ + /* Only opers can change other users modes */ + if ((source != dest) && (!*source->oper)) + return MODEACTION_DENY; + + /* Set the bitfields */ + if (dest->modes[UM_SERVERNOTICE] != adding) + { + dest->modes[UM_SERVERNOTICE] = adding; + this->count += (adding ? 1: -1); + return MODEACTION_ALLOW; + } + + /* Allow the change */ + return MODEACTION_DENY; +} + +unsigned int ModeUserServerNotice::GetCount() +{ + return count; +} diff --git a/src/modes/umode_w.cpp b/src/modes/umode_w.cpp index 21c007655..383c91f6e 100644 --- a/src/modes/umode_w.cpp +++ b/src/modes/umode_w.cpp @@ -1 +1,46 @@ -/* +------------------------------------+
* | 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 "mode.h"
#include "channels.h"
#include "users.h"
#include "modes/umode_w.h"
ModeUserWallops::ModeUserWallops(InspIRCd* Instance) : ModeHandler(Instance, 'w', 0, 0, false, MODETYPE_USER, false)
{
}
ModeAction ModeUserWallops::OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string ¶meter, bool adding)
{
/* Only opers can change other users modes */
if ((source != dest) && (!*source->oper))
return MODEACTION_DENY;
/* Set the bitfields */
if (dest->modes[UM_WALLOPS] != adding)
{
dest->modes[UM_WALLOPS] = adding;
this->count += (adding ? 1: -1);
return MODEACTION_ALLOW;
}
/* Allow the change */
return MODEACTION_DENY;
}
unsigned int ModeUserWallops::GetCount()
{
return count;
}
\ No newline at end of file +/* +------------------------------------+ + * | 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 "mode.h" +#include "channels.h" +#include "users.h" +#include "modes/umode_w.h" + +ModeUserWallops::ModeUserWallops(InspIRCd* Instance) : ModeHandler(Instance, 'w', 0, 0, false, MODETYPE_USER, false) +{ +} + +ModeAction ModeUserWallops::OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string ¶meter, bool adding) +{ + /* Only opers can change other users modes */ + if ((source != dest) && (!*source->oper)) + return MODEACTION_DENY; + + /* Set the bitfields */ + if (dest->modes[UM_WALLOPS] != adding) + { + dest->modes[UM_WALLOPS] = adding; + this->count += (adding ? 1: -1); + return MODEACTION_ALLOW; + } + + /* Allow the change */ + return MODEACTION_DENY; +} + +unsigned int ModeUserWallops::GetCount() +{ + return count; +} + diff --git a/src/modules.cpp b/src/modules.cpp index 2c34a034c..9deaa7954 100644 --- a/src/modules.cpp +++ b/src/modules.cpp @@ -1 +1,732 @@ -/* +------------------------------------+
* | 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 "users.h"
#include "modules.h"
#include "wildcard.h"
#include "mode.h"
#include "xline.h"
#include "socket.h"
#include "socketengine.h"
#include "command_parse.h"
#include "dns.h"
// version is a simple class for holding a modules version number
Version::Version(int major, int minor, int revision, int build, int flags, int api_ver)
: Major(major), Minor(minor), Revision(revision), Build(build), Flags(flags), API(api_ver)
{
}
Request::Request(char* anydata, Module* src, Module* dst)
: data(anydata), source(src), dest(dst)
{
/* Ensure that because this module doesnt support ID strings, it doesnt break modules that do
* by passing them uninitialized pointers (could happen)
*/
id = '\0';
}
Request::Request(Module* src, Module* dst, const char* idstr)
: id(idstr), source(src), dest(dst)
{
}
char* Request::GetData()
{
return this->data;
}
const char* Request::GetId()
{
return this->id;
}
Module* Request::GetSource()
{
return this->source;
}
Module* Request::GetDest()
{
return this->dest;
}
char* Request::Send()
{
if (this->dest)
{
return dest->OnRequest(this);
}
else
{
return NULL;
}
}
Event::Event(char* anydata, Module* src, const std::string &eventid) : data(anydata), source(src), id(eventid) { }
char* Event::GetData()
{
return (char*)this->data;
}
Module* Event::GetSource()
{
return this->source;
}
char* Event::Send(InspIRCd* ServerInstance)
{
FOREACH_MOD(I_OnEvent,OnEvent(this));
return NULL;
}
std::string Event::GetEventID()
{
return this->id;
}
// These declarations define the behavours of the base class Module (which does nothing at all)
Module::Module(InspIRCd* Me) : ServerInstance(Me) { }
Module::~Module() { }
void Module::OnUserConnect(userrec* user) { }
void Module::OnUserQuit(userrec* user, const std::string& message, const std::string &oper_message) { }
void Module::OnUserDisconnect(userrec* user) { }
void Module::OnUserJoin(userrec* user, chanrec* channel, bool &silent) { }
void Module::OnPostJoin(userrec* user, chanrec* channel) { }
void Module::OnUserPart(userrec* user, chanrec* channel, const std::string &partmessage, bool &silent) { }
void Module::OnRehash(userrec* user, const std::string ¶meter) { }
void Module::OnServerRaw(std::string &raw, bool inbound, userrec* user) { }
int Module::OnUserPreJoin(userrec* user, chanrec* chan, const char* cname, std::string &privs) { return 0; }
void Module::OnMode(userrec* user, void* dest, int target_type, const std::string &text) { }
Version Module::GetVersion() { return Version(1,0,0,0,VF_VENDOR,-1); }
void Module::OnOper(userrec* user, const std::string &opertype) { }
void Module::OnPostOper(userrec* user, const std::string &opertype) { }
void Module::OnInfo(userrec* user) { }
void Module::OnWhois(userrec* source, userrec* dest) { }
int Module::OnUserPreInvite(userrec* source,userrec* dest,chanrec* channel) { return 0; }
int Module::OnUserPreMessage(userrec* user,void* dest,int target_type, std::string &text,char status, CUList &exempt_list) { return 0; }
int Module::OnUserPreNotice(userrec* user,void* dest,int target_type, std::string &text,char status, CUList &exempt_list) { return 0; }
int Module::OnUserPreNick(userrec* user, const std::string &newnick) { return 0; }
void Module::OnUserPostNick(userrec* user, const std::string &oldnick) { }
int Module::OnAccessCheck(userrec* source,userrec* dest,chanrec* channel,int access_type) { return ACR_DEFAULT; }
void Module::On005Numeric(std::string &output) { }
int Module::OnKill(userrec* source, userrec* dest, const std::string &reason) { return 0; }
void Module::OnLoadModule(Module* mod,const std::string &name) { }
void Module::OnUnloadModule(Module* mod,const std::string &name) { }
void Module::OnBackgroundTimer(time_t curtime) { }
int Module::OnPreCommand(const std::string &command, const char** parameters, int pcnt, userrec *user, bool validated, const std::string &original_line) { return 0; }
void Module::OnPostCommand(const std::string &command, const char** parameters, int pcnt, userrec *user, CmdResult result, const std::string &original_line) { }
bool Module::OnCheckReady(userrec* user) { return true; }
int Module::OnUserRegister(userrec* user) { return 0; }
int Module::OnUserPreKick(userrec* source, userrec* user, chanrec* chan, const std::string &reason) { return 0; }
void Module::OnUserKick(userrec* source, userrec* user, chanrec* chan, const std::string &reason, bool &silent) { }
int Module::OnCheckInvite(userrec* user, chanrec* chan) { return 0; }
int Module::OnCheckKey(userrec* user, chanrec* chan, const std::string &keygiven) { return 0; }
int Module::OnCheckLimit(userrec* user, chanrec* chan) { return 0; }
int Module::OnCheckBan(userrec* user, chanrec* chan) { return 0; }
int Module::OnStats(char symbol, userrec* user, string_list &results) { return 0; }
int Module::OnChangeLocalUserHost(userrec* user, const std::string &newhost) { return 0; }
int Module::OnChangeLocalUserGECOS(userrec* user, const std::string &newhost) { return 0; }
int Module::OnLocalTopicChange(userrec* user, chanrec* chan, const std::string &topic) { return 0; }
void Module::OnEvent(Event* event) { return; }
char* Module::OnRequest(Request* request) { return NULL; }
int Module::OnOperCompare(const std::string &password, const std::string &input, int tagnumber) { return 0; }
void Module::OnGlobalOper(userrec* user) { }
void Module::OnPostConnect(userrec* user) { }
int Module::OnAddBan(userrec* source, chanrec* channel,const std::string &banmask) { return 0; }
int Module::OnDelBan(userrec* source, chanrec* channel,const std::string &banmask) { return 0; }
void Module::OnRawSocketAccept(int fd, const std::string &ip, int localport) { }
int Module::OnRawSocketWrite(int fd, const char* buffer, int count) { return 0; }
void Module::OnRawSocketClose(int fd) { }
void Module::OnRawSocketConnect(int fd) { }
int Module::OnRawSocketRead(int fd, char* buffer, unsigned int count, int &readresult) { return 0; }
void Module::OnUserMessage(userrec* user, void* dest, int target_type, const std::string &text, char status, const CUList &exempt_list) { }
void Module::OnUserNotice(userrec* user, void* dest, int target_type, const std::string &text, char status, const CUList &exempt_list) { }
void Module::OnRemoteKill(userrec* source, userrec* dest, const std::string &reason, const std::string &operreason) { }
void Module::OnUserInvite(userrec* source,userrec* dest,chanrec* channel) { }
void Module::OnPostLocalTopicChange(userrec* user, chanrec* chan, const std::string &topic) { }
void Module::OnGetServerDescription(const std::string &servername,std::string &description) { }
void Module::OnSyncUser(userrec* user, Module* proto, void* opaque) { }
void Module::OnSyncChannel(chanrec* chan, Module* proto, void* opaque) { }
void Module::ProtoSendMode(void* opaque, int target_type, void* target, const std::string &modeline) { }
void Module::OnSyncChannelMetaData(chanrec* chan, Module* proto,void* opaque, const std::string &extname, bool displayable) { }
void Module::OnSyncUserMetaData(userrec* user, Module* proto,void* opaque, const std::string &extname, bool displayable) { }
void Module::OnSyncOtherMetaData(Module* proto, void* opaque, bool displayable) { }
void Module::OnDecodeMetaData(int target_type, void* target, const std::string &extname, const std::string &extdata) { }
void Module::ProtoSendMetaData(void* opaque, int target_type, void* target, const std::string &extname, const std::string &extdata) { }
void Module::OnWallops(userrec* user, const std::string &text) { }
void Module::OnChangeHost(userrec* user, const std::string &newhost) { }
void Module::OnChangeName(userrec* user, const std::string &gecos) { }
void Module::OnAddGLine(long duration, userrec* source, const std::string &reason, const std::string &hostmask) { }
void Module::OnAddZLine(long duration, userrec* source, const std::string &reason, const std::string &ipmask) { }
void Module::OnAddKLine(long duration, userrec* source, const std::string &reason, const std::string &hostmask) { }
void Module::OnAddQLine(long duration, userrec* source, const std::string &reason, const std::string &nickmask) { }
void Module::OnAddELine(long duration, userrec* source, const std::string &reason, const std::string &hostmask) { }
void Module::OnDelGLine(userrec* source, const std::string &hostmask) { }
void Module::OnDelZLine(userrec* source, const std::string &ipmask) { }
void Module::OnDelKLine(userrec* source, const std::string &hostmask) { }
void Module::OnDelQLine(userrec* source, const std::string &nickmask) { }
void Module::OnDelELine(userrec* source, const std::string &hostmask) { }
void Module::OnCleanup(int target_type, void* item) { }
void Module::Implements(char* Implements) { for (int j = 0; j < 255; j++) Implements[j] = 0; }
void Module::OnChannelDelete(chanrec* chan) { }
Priority Module::Prioritize() { return PRIORITY_DONTCARE; }
void Module::OnSetAway(userrec* user) { }
void Module::OnCancelAway(userrec* user) { }
int Module::OnUserList(userrec* user, chanrec* Ptr, CUList* &userlist) { return 0; }
int Module::OnWhoisLine(userrec* user, userrec* dest, int &numeric, std::string &text) { return 0; }
void Module::OnBuildExemptList(MessageType message_type, chanrec* chan, userrec* sender, char status, CUList &exempt_list) { }
void Module::OnGarbageCollect() { }
void Module::OnBufferFlushed(userrec* user) { }
long InspIRCd::PriorityAfter(const std::string &modulename)
{
for (unsigned int j = 0; j < this->Config->module_names.size(); j++)
{
if (this->Config->module_names[j] == modulename)
{
return ((j << 8) | PRIORITY_AFTER);
}
}
return PRIORITY_DONTCARE;
}
long InspIRCd::PriorityBefore(const std::string &modulename)
{
for (unsigned int j = 0; j < this->Config->module_names.size(); j++)
{
if (this->Config->module_names[j] == modulename)
{
return ((j << 8) | PRIORITY_BEFORE);
}
}
return PRIORITY_DONTCARE;
}
bool InspIRCd::PublishFeature(const std::string &FeatureName, Module* Mod)
{
if (Features.find(FeatureName) == Features.end())
{
Features[FeatureName] = Mod;
return true;
}
return false;
}
bool InspIRCd::UnpublishFeature(const std::string &FeatureName)
{
featurelist::iterator iter = Features.find(FeatureName);
if (iter == Features.end())
return false;
Features.erase(iter);
return true;
}
Module* InspIRCd::FindFeature(const std::string &FeatureName)
{
featurelist::iterator iter = Features.find(FeatureName);
if (iter == Features.end())
return NULL;
return iter->second;
}
bool InspIRCd::PublishInterface(const std::string &InterfaceName, Module* Mod)
{
interfacelist::iterator iter = Interfaces.find(InterfaceName);
if (iter == Interfaces.end())
{
modulelist ml;
ml.push_back(Mod);
Interfaces[InterfaceName] = std::make_pair(0, ml);
return true;
}
else
{
iter->second.second.push_back(Mod);
return true;
}
return false;
}
bool InspIRCd::UnpublishInterface(const std::string &InterfaceName, Module* Mod)
{
interfacelist::iterator iter = Interfaces.find(InterfaceName);
if (iter == Interfaces.end())
return false;
for (modulelist::iterator x = iter->second.second.begin(); x != iter->second.second.end(); x++)
{
if (*x == Mod)
{
iter->second.second.erase(x);
if (iter->second.second.empty())
Interfaces.erase(InterfaceName);
return true;
}
}
return false;
}
modulelist* InspIRCd::FindInterface(const std::string &InterfaceName)
{
interfacelist::iterator iter = Interfaces.find(InterfaceName);
if (iter == Interfaces.end())
return NULL;
else
return &(iter->second.second);
}
void InspIRCd::UseInterface(const std::string &InterfaceName)
{
interfacelist::iterator iter = Interfaces.find(InterfaceName);
if (iter != Interfaces.end())
iter->second.first++;
}
void InspIRCd::DoneWithInterface(const std::string &InterfaceName)
{
interfacelist::iterator iter = Interfaces.find(InterfaceName);
if (iter != Interfaces.end())
iter->second.first--;
}
std::pair<int,std::string> InspIRCd::GetInterfaceInstanceCount(Module* m)
{
for (interfacelist::iterator iter = Interfaces.begin(); iter != Interfaces.end(); iter++)
{
for (modulelist::iterator x = iter->second.second.begin(); x != iter->second.second.end(); x++)
{
if (*x == m)
{
return std::make_pair(iter->second.first, iter->first);
}
}
}
return std::make_pair(0, "");
}
const std::string& InspIRCd::GetModuleName(Module* m)
{
static std::string nothing; /* Prevent compiler warning */
if (!this->GetModuleCount())
return nothing;
for (int i = 0; i <= this->GetModuleCount(); i++)
{
if (this->modules[i] == m)
{
return this->Config->module_names[i];
}
}
return nothing; /* As above */
}
void InspIRCd::RehashServer()
{
this->WriteOpers("*** Rehashing config file");
this->RehashUsersAndChans();
this->Config->Read(false,NULL);
this->ResetMaxBans();
this->Res->Rehash();
}
/* This is ugly, yes, but hash_map's arent designed to be
* addressed in this manner, and this is a bit of a kludge.
* Luckily its a specialist function and rarely used by
* many modules (in fact, it was specially created to make
* m_safelist possible, initially).
*/
chanrec* InspIRCd::GetChannelIndex(long index)
{
int target = 0;
for (chan_hash::iterator n = this->chanlist->begin(); n != this->chanlist->end(); n++, target++)
{
if (index == target)
return n->second;
}
return NULL;
}
bool InspIRCd::MatchText(const std::string &sliteral, const std::string &spattern)
{
return match(sliteral.c_str(),spattern.c_str());
}
CmdResult InspIRCd::CallCommandHandler(const std::string &commandname, const char** parameters, int pcnt, userrec* user)
{
return this->Parser->CallHandler(commandname,parameters,pcnt,user);
}
bool InspIRCd::IsValidModuleCommand(const std::string &commandname, int pcnt, userrec* user)
{
return this->Parser->IsValidCommand(commandname, pcnt, user);
}
void InspIRCd::AddCommand(command_t *f)
{
if (!this->Parser->CreateCommand(f))
{
ModuleException err("Command "+std::string(f->command)+" already exists.");
throw (err);
}
}
void InspIRCd::SendMode(const char** parameters, int pcnt, userrec *user)
{
this->Modes->Process(parameters,pcnt,user,true);
}
void InspIRCd::DumpText(userrec* User, const std::string &LinePrefix, stringstream &TextStream)
{
std::string CompleteLine = LinePrefix;
std::string Word;
while (TextStream >> Word)
{
if (CompleteLine.length() + Word.length() + 3 > 500)
{
User->WriteServ(CompleteLine);
CompleteLine = LinePrefix;
}
CompleteLine = CompleteLine + Word + " ";
}
User->WriteServ(CompleteLine);
}
userrec* InspIRCd::FindDescriptor(int socket)
{
return reinterpret_cast<userrec*>(this->SE->GetRef(socket));
}
bool InspIRCd::AddMode(ModeHandler* mh, const unsigned char mode)
{
return this->Modes->AddMode(mh,mode);
}
bool InspIRCd::AddModeWatcher(ModeWatcher* mw)
{
return this->Modes->AddModeWatcher(mw);
}
bool InspIRCd::DelModeWatcher(ModeWatcher* mw)
{
return this->Modes->DelModeWatcher(mw);
}
bool InspIRCd::AddResolver(Resolver* r, bool cached)
{
if (!cached)
return this->Res->AddResolverClass(r);
else
{
r->TriggerCachedResult();
delete r;
return true;
}
}
void InspIRCd::AddGLine(long duration, const std::string &source, const std::string &reason, const std::string &hostmask)
{
XLines->add_gline(duration, source.c_str(), reason.c_str(), hostmask.c_str());
XLines->apply_lines(APPLY_GLINES);
}
void InspIRCd::AddQLine(long duration, const std::string &source, const std::string &reason, const std::string &nickname)
{
XLines->add_qline(duration, source.c_str(), reason.c_str(), nickname.c_str());
XLines->apply_lines(APPLY_QLINES);
}
void InspIRCd::AddZLine(long duration, const std::string &source, const std::string &reason, const std::string &ipaddr)
{
XLines->add_zline(duration, source.c_str(), reason.c_str(), ipaddr.c_str());
XLines->apply_lines(APPLY_ZLINES);
}
void InspIRCd::AddKLine(long duration, const std::string &source, const std::string &reason, const std::string &hostmask)
{
XLines->add_kline(duration, source.c_str(), reason.c_str(), hostmask.c_str());
XLines->apply_lines(APPLY_KLINES);
}
void InspIRCd::AddELine(long duration, const std::string &source, const std::string &reason, const std::string &hostmask)
{
XLines->add_eline(duration, source.c_str(), reason.c_str(), hostmask.c_str());
}
bool InspIRCd::DelGLine(const std::string &hostmask)
{
return XLines->del_gline(hostmask.c_str());
}
bool InspIRCd::DelQLine(const std::string &nickname)
{
return XLines->del_qline(nickname.c_str());
}
bool InspIRCd::DelZLine(const std::string &ipaddr)
{
return XLines->del_zline(ipaddr.c_str());
}
bool InspIRCd::DelKLine(const std::string &hostmask)
{
return XLines->del_kline(hostmask.c_str());
}
bool InspIRCd::DelELine(const std::string &hostmask)
{
return XLines->del_eline(hostmask.c_str());
}
/*
* XXX why on *earth* is this in modules.cpp...? I think
* perhaps we need a server.cpp for InspIRCd:: stuff where possible. -- w00t
*/
bool InspIRCd::IsValidMask(const std::string &mask)
{
char* dest = (char*)mask.c_str();
if (strchr(dest,'!')==0)
return false;
if (strchr(dest,'@')==0)
return false;
for (char* i = dest; *i; i++)
if (*i < 32)
return false;
for (char* i = dest; *i; i++)
if (*i > 126)
return false;
unsigned int c = 0;
for (char* i = dest; *i; i++)
if (*i == '!')
c++;
if (c>1)
return false;
c = 0;
for (char* i = dest; *i; i++)
if (*i == '@')
c++;
if (c>1)
return false;
return true;
}
Module* InspIRCd::FindModule(const std::string &name)
{
for (int i = 0; i <= this->GetModuleCount(); i++)
{
if (this->Config->module_names[i] == name)
{
return this->modules[i];
}
}
return NULL;
}
ConfigReader::ConfigReader(InspIRCd* Instance) : ServerInstance(Instance)
{
/* Is there any reason to load the entire config file again here?
* it's needed if they specify another config file, but using the
* default one we can just use the global config data - pre-parsed!
*/
this->errorlog = new std::ostringstream(std::stringstream::in | std::stringstream::out);
this->data = &ServerInstance->Config->config_data;
this->privatehash = false;
}
ConfigReader::~ConfigReader()
{
if (this->errorlog)
DELETE(this->errorlog);
if(this->privatehash)
DELETE(this->data);
}
ConfigReader::ConfigReader(InspIRCd* Instance, const std::string &filename) : ServerInstance(Instance)
{
ServerInstance->Config->ClearStack();
this->data = new ConfigDataHash;
this->privatehash = true;
this->errorlog = new std::ostringstream(std::stringstream::in | std::stringstream::out);
this->readerror = ServerInstance->Config->LoadConf(*this->data, filename, *this->errorlog);
if (!this->readerror)
this->error = CONF_FILE_NOT_FOUND;
}
std::string ConfigReader::ReadValue(const std::string &tag, const std::string &name, const std::string &default_value, int index, bool allow_linefeeds)
{
/* Don't need to strlcpy() tag and name anymore, ReadConf() takes const char* */
std::string result;
if (!ServerInstance->Config->ConfValue(*this->data, tag, name, default_value, index, result, allow_linefeeds))
{
this->error = CONF_VALUE_NOT_FOUND;
}
return result;
}
std::string ConfigReader::ReadValue(const std::string &tag, const std::string &name, int index, bool allow_linefeeds)
{
return ReadValue(tag, name, "", index, allow_linefeeds);
}
bool ConfigReader::ReadFlag(const std::string &tag, const std::string &name, const std::string &default_value, int index)
{
return ServerInstance->Config->ConfValueBool(*this->data, tag, name, default_value, index);
}
bool ConfigReader::ReadFlag(const std::string &tag, const std::string &name, int index)
{
return ReadFlag(tag, name, "", index);
}
long ConfigReader::ReadInteger(const std::string &tag, const std::string &name, const std::string &default_value, int index, bool needs_unsigned)
{
int result;
if(!ServerInstance->Config->ConfValueInteger(*this->data, tag, name, default_value, index, result))
{
this->error = CONF_VALUE_NOT_FOUND;
return 0;
}
if ((needs_unsigned) && (result < 0))
{
this->error = CONF_NOT_UNSIGNED;
return 0;
}
return result;
}
long ConfigReader::ReadInteger(const std::string &tag, const std::string &name, int index, bool needs_unsigned)
{
return ReadInteger(tag, name, "", index, needs_unsigned);
}
long ConfigReader::GetError()
{
long olderr = this->error;
this->error = 0;
return olderr;
}
void ConfigReader::DumpErrors(bool bail, userrec* user)
{
ServerInstance->Config->ReportConfigError(this->errorlog->str(), bail, user);
}
int ConfigReader::Enumerate(const std::string &tag)
{
return ServerInstance->Config->ConfValueEnum(*this->data, tag);
}
int ConfigReader::EnumerateValues(const std::string &tag, int index)
{
return ServerInstance->Config->ConfVarEnum(*this->data, tag, index);
}
bool ConfigReader::Verify()
{
return this->readerror;
}
FileReader::FileReader(InspIRCd* Instance, const std::string &filename) : ServerInstance(Instance)
{
LoadFile(filename);
}
FileReader::FileReader(InspIRCd* Instance) : ServerInstance(Instance)
{
}
std::string FileReader::Contents()
{
std::string x;
for (file_cache::iterator a = this->fc.begin(); a != this->fc.end(); a++)
{
x.append(*a);
x.append("\r\n");
}
return x;
}
unsigned long FileReader::ContentSize()
{
return this->contentsize;
}
void FileReader::CalcSize()
{
unsigned long n = 0;
for (file_cache::iterator a = this->fc.begin(); a != this->fc.end(); a++)
n += (a->length() + 2);
this->contentsize = n;
}
void FileReader::LoadFile(const std::string &filename)
{
file_cache c;
c.clear();
if (ServerInstance->Config->ReadFile(c,filename.c_str()))
{
this->fc = c;
this->CalcSize();
}
}
FileReader::~FileReader()
{
}
bool FileReader::Exists()
{
return (!(fc.size() == 0));
}
std::string FileReader::GetLine(int x)
{
if ((x<0) || ((unsigned)x>fc.size()))
return "";
return fc[x];
}
int FileReader::FileSize()
{
return fc.size();
}
\ No newline at end of file +/* +------------------------------------+ + * | 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 "users.h" +#include "modules.h" +#include "wildcard.h" +#include "mode.h" +#include "xline.h" +#include "socket.h" +#include "socketengine.h" +#include "command_parse.h" +#include "dns.h" + +// version is a simple class for holding a modules version number +Version::Version(int major, int minor, int revision, int build, int flags, int api_ver) +: Major(major), Minor(minor), Revision(revision), Build(build), Flags(flags), API(api_ver) +{ +} + +Request::Request(char* anydata, Module* src, Module* dst) +: data(anydata), source(src), dest(dst) +{ + /* Ensure that because this module doesnt support ID strings, it doesnt break modules that do + * by passing them uninitialized pointers (could happen) + */ + id = '\0'; +} + +Request::Request(Module* src, Module* dst, const char* idstr) +: id(idstr), source(src), dest(dst) +{ +} + +char* Request::GetData() +{ + return this->data; +} + +const char* Request::GetId() +{ + return this->id; +} + +Module* Request::GetSource() +{ + return this->source; +} + +Module* Request::GetDest() +{ + return this->dest; +} + +char* Request::Send() +{ + if (this->dest) + { + return dest->OnRequest(this); + } + else + { + return NULL; + } +} + +Event::Event(char* anydata, Module* src, const std::string &eventid) : data(anydata), source(src), id(eventid) { } + +char* Event::GetData() +{ + return (char*)this->data; +} + +Module* Event::GetSource() +{ + return this->source; +} + +char* Event::Send(InspIRCd* ServerInstance) +{ + FOREACH_MOD(I_OnEvent,OnEvent(this)); + return NULL; +} + +std::string Event::GetEventID() +{ + return this->id; +} + + +// These declarations define the behavours of the base class Module (which does nothing at all) + + Module::Module(InspIRCd* Me) : ServerInstance(Me) { } + Module::~Module() { } +void Module::OnUserConnect(userrec* user) { } +void Module::OnUserQuit(userrec* user, const std::string& message, const std::string &oper_message) { } +void Module::OnUserDisconnect(userrec* user) { } +void Module::OnUserJoin(userrec* user, chanrec* channel, bool &silent) { } +void Module::OnPostJoin(userrec* user, chanrec* channel) { } +void Module::OnUserPart(userrec* user, chanrec* channel, const std::string &partmessage, bool &silent) { } +void Module::OnRehash(userrec* user, const std::string ¶meter) { } +void Module::OnServerRaw(std::string &raw, bool inbound, userrec* user) { } +int Module::OnUserPreJoin(userrec* user, chanrec* chan, const char* cname, std::string &privs) { return 0; } +void Module::OnMode(userrec* user, void* dest, int target_type, const std::string &text) { } +Version Module::GetVersion() { return Version(1,0,0,0,VF_VENDOR,-1); } +void Module::OnOper(userrec* user, const std::string &opertype) { } +void Module::OnPostOper(userrec* user, const std::string &opertype) { } +void Module::OnInfo(userrec* user) { } +void Module::OnWhois(userrec* source, userrec* dest) { } +int Module::OnUserPreInvite(userrec* source,userrec* dest,chanrec* channel) { return 0; } +int Module::OnUserPreMessage(userrec* user,void* dest,int target_type, std::string &text,char status, CUList &exempt_list) { return 0; } +int Module::OnUserPreNotice(userrec* user,void* dest,int target_type, std::string &text,char status, CUList &exempt_list) { return 0; } +int Module::OnUserPreNick(userrec* user, const std::string &newnick) { return 0; } +void Module::OnUserPostNick(userrec* user, const std::string &oldnick) { } +int Module::OnAccessCheck(userrec* source,userrec* dest,chanrec* channel,int access_type) { return ACR_DEFAULT; } +void Module::On005Numeric(std::string &output) { } +int Module::OnKill(userrec* source, userrec* dest, const std::string &reason) { return 0; } +void Module::OnLoadModule(Module* mod,const std::string &name) { } +void Module::OnUnloadModule(Module* mod,const std::string &name) { } +void Module::OnBackgroundTimer(time_t curtime) { } +int Module::OnPreCommand(const std::string &command, const char** parameters, int pcnt, userrec *user, bool validated, const std::string &original_line) { return 0; } +void Module::OnPostCommand(const std::string &command, const char** parameters, int pcnt, userrec *user, CmdResult result, const std::string &original_line) { } +bool Module::OnCheckReady(userrec* user) { return true; } +int Module::OnUserRegister(userrec* user) { return 0; } +int Module::OnUserPreKick(userrec* source, userrec* user, chanrec* chan, const std::string &reason) { return 0; } +void Module::OnUserKick(userrec* source, userrec* user, chanrec* chan, const std::string &reason, bool &silent) { } +int Module::OnCheckInvite(userrec* user, chanrec* chan) { return 0; } +int Module::OnCheckKey(userrec* user, chanrec* chan, const std::string &keygiven) { return 0; } +int Module::OnCheckLimit(userrec* user, chanrec* chan) { return 0; } +int Module::OnCheckBan(userrec* user, chanrec* chan) { return 0; } +int Module::OnStats(char symbol, userrec* user, string_list &results) { return 0; } +int Module::OnChangeLocalUserHost(userrec* user, const std::string &newhost) { return 0; } +int Module::OnChangeLocalUserGECOS(userrec* user, const std::string &newhost) { return 0; } +int Module::OnLocalTopicChange(userrec* user, chanrec* chan, const std::string &topic) { return 0; } +void Module::OnEvent(Event* event) { return; } +char* Module::OnRequest(Request* request) { return NULL; } +int Module::OnOperCompare(const std::string &password, const std::string &input, int tagnumber) { return 0; } +void Module::OnGlobalOper(userrec* user) { } +void Module::OnPostConnect(userrec* user) { } +int Module::OnAddBan(userrec* source, chanrec* channel,const std::string &banmask) { return 0; } +int Module::OnDelBan(userrec* source, chanrec* channel,const std::string &banmask) { return 0; } +void Module::OnRawSocketAccept(int fd, const std::string &ip, int localport) { } +int Module::OnRawSocketWrite(int fd, const char* buffer, int count) { return 0; } +void Module::OnRawSocketClose(int fd) { } +void Module::OnRawSocketConnect(int fd) { } +int Module::OnRawSocketRead(int fd, char* buffer, unsigned int count, int &readresult) { return 0; } +void Module::OnUserMessage(userrec* user, void* dest, int target_type, const std::string &text, char status, const CUList &exempt_list) { } +void Module::OnUserNotice(userrec* user, void* dest, int target_type, const std::string &text, char status, const CUList &exempt_list) { } +void Module::OnRemoteKill(userrec* source, userrec* dest, const std::string &reason, const std::string &operreason) { } +void Module::OnUserInvite(userrec* source,userrec* dest,chanrec* channel) { } +void Module::OnPostLocalTopicChange(userrec* user, chanrec* chan, const std::string &topic) { } +void Module::OnGetServerDescription(const std::string &servername,std::string &description) { } +void Module::OnSyncUser(userrec* user, Module* proto, void* opaque) { } +void Module::OnSyncChannel(chanrec* chan, Module* proto, void* opaque) { } +void Module::ProtoSendMode(void* opaque, int target_type, void* target, const std::string &modeline) { } +void Module::OnSyncChannelMetaData(chanrec* chan, Module* proto,void* opaque, const std::string &extname, bool displayable) { } +void Module::OnSyncUserMetaData(userrec* user, Module* proto,void* opaque, const std::string &extname, bool displayable) { } +void Module::OnSyncOtherMetaData(Module* proto, void* opaque, bool displayable) { } +void Module::OnDecodeMetaData(int target_type, void* target, const std::string &extname, const std::string &extdata) { } +void Module::ProtoSendMetaData(void* opaque, int target_type, void* target, const std::string &extname, const std::string &extdata) { } +void Module::OnWallops(userrec* user, const std::string &text) { } +void Module::OnChangeHost(userrec* user, const std::string &newhost) { } +void Module::OnChangeName(userrec* user, const std::string &gecos) { } +void Module::OnAddGLine(long duration, userrec* source, const std::string &reason, const std::string &hostmask) { } +void Module::OnAddZLine(long duration, userrec* source, const std::string &reason, const std::string &ipmask) { } +void Module::OnAddKLine(long duration, userrec* source, const std::string &reason, const std::string &hostmask) { } +void Module::OnAddQLine(long duration, userrec* source, const std::string &reason, const std::string &nickmask) { } +void Module::OnAddELine(long duration, userrec* source, const std::string &reason, const std::string &hostmask) { } +void Module::OnDelGLine(userrec* source, const std::string &hostmask) { } +void Module::OnDelZLine(userrec* source, const std::string &ipmask) { } +void Module::OnDelKLine(userrec* source, const std::string &hostmask) { } +void Module::OnDelQLine(userrec* source, const std::string &nickmask) { } +void Module::OnDelELine(userrec* source, const std::string &hostmask) { } +void Module::OnCleanup(int target_type, void* item) { } +void Module::Implements(char* Implements) { for (int j = 0; j < 255; j++) Implements[j] = 0; } +void Module::OnChannelDelete(chanrec* chan) { } +Priority Module::Prioritize() { return PRIORITY_DONTCARE; } +void Module::OnSetAway(userrec* user) { } +void Module::OnCancelAway(userrec* user) { } +int Module::OnUserList(userrec* user, chanrec* Ptr, CUList* &userlist) { return 0; } +int Module::OnWhoisLine(userrec* user, userrec* dest, int &numeric, std::string &text) { return 0; } +void Module::OnBuildExemptList(MessageType message_type, chanrec* chan, userrec* sender, char status, CUList &exempt_list) { } +void Module::OnGarbageCollect() { } +void Module::OnBufferFlushed(userrec* user) { } + +long InspIRCd::PriorityAfter(const std::string &modulename) +{ + for (unsigned int j = 0; j < this->Config->module_names.size(); j++) + { + if (this->Config->module_names[j] == modulename) + { + return ((j << 8) | PRIORITY_AFTER); + } + } + return PRIORITY_DONTCARE; +} + +long InspIRCd::PriorityBefore(const std::string &modulename) +{ + for (unsigned int j = 0; j < this->Config->module_names.size(); j++) + { + if (this->Config->module_names[j] == modulename) + { + return ((j << 8) | PRIORITY_BEFORE); + } + } + return PRIORITY_DONTCARE; +} + +bool InspIRCd::PublishFeature(const std::string &FeatureName, Module* Mod) +{ + if (Features.find(FeatureName) == Features.end()) + { + Features[FeatureName] = Mod; + return true; + } + return false; +} + +bool InspIRCd::UnpublishFeature(const std::string &FeatureName) +{ + featurelist::iterator iter = Features.find(FeatureName); + + if (iter == Features.end()) + return false; + + Features.erase(iter); + return true; +} + +Module* InspIRCd::FindFeature(const std::string &FeatureName) +{ + featurelist::iterator iter = Features.find(FeatureName); + + if (iter == Features.end()) + return NULL; + + return iter->second; +} + +bool InspIRCd::PublishInterface(const std::string &InterfaceName, Module* Mod) +{ + interfacelist::iterator iter = Interfaces.find(InterfaceName); + + if (iter == Interfaces.end()) + { + modulelist ml; + ml.push_back(Mod); + Interfaces[InterfaceName] = std::make_pair(0, ml); + return true; + } + else + { + iter->second.second.push_back(Mod); + return true; + } + return false; +} + +bool InspIRCd::UnpublishInterface(const std::string &InterfaceName, Module* Mod) +{ + interfacelist::iterator iter = Interfaces.find(InterfaceName); + + if (iter == Interfaces.end()) + return false; + + for (modulelist::iterator x = iter->second.second.begin(); x != iter->second.second.end(); x++) + { + if (*x == Mod) + { + iter->second.second.erase(x); + if (iter->second.second.empty()) + Interfaces.erase(InterfaceName); + return true; + } + } + return false; +} + +modulelist* InspIRCd::FindInterface(const std::string &InterfaceName) +{ + interfacelist::iterator iter = Interfaces.find(InterfaceName); + if (iter == Interfaces.end()) + return NULL; + else + return &(iter->second.second); +} + +void InspIRCd::UseInterface(const std::string &InterfaceName) +{ + interfacelist::iterator iter = Interfaces.find(InterfaceName); + if (iter != Interfaces.end()) + iter->second.first++; + +} + +void InspIRCd::DoneWithInterface(const std::string &InterfaceName) +{ + interfacelist::iterator iter = Interfaces.find(InterfaceName); + if (iter != Interfaces.end()) + iter->second.first--; +} + +std::pair<int,std::string> InspIRCd::GetInterfaceInstanceCount(Module* m) +{ + for (interfacelist::iterator iter = Interfaces.begin(); iter != Interfaces.end(); iter++) + { + for (modulelist::iterator x = iter->second.second.begin(); x != iter->second.second.end(); x++) + { + if (*x == m) + { + return std::make_pair(iter->second.first, iter->first); + } + } + } + return std::make_pair(0, ""); +} + +const std::string& InspIRCd::GetModuleName(Module* m) +{ + static std::string nothing; /* Prevent compiler warning */ + + if (!this->GetModuleCount()) + return nothing; + + for (int i = 0; i <= this->GetModuleCount(); i++) + { + if (this->modules[i] == m) + { + return this->Config->module_names[i]; + } + } + return nothing; /* As above */ +} + +void InspIRCd::RehashServer() +{ + this->WriteOpers("*** Rehashing config file"); + this->RehashUsersAndChans(); + this->Config->Read(false,NULL); + this->ResetMaxBans(); + this->Res->Rehash(); +} + +/* This is ugly, yes, but hash_map's arent designed to be + * addressed in this manner, and this is a bit of a kludge. + * Luckily its a specialist function and rarely used by + * many modules (in fact, it was specially created to make + * m_safelist possible, initially). + */ + +chanrec* InspIRCd::GetChannelIndex(long index) +{ + int target = 0; + for (chan_hash::iterator n = this->chanlist->begin(); n != this->chanlist->end(); n++, target++) + { + if (index == target) + return n->second; + } + return NULL; +} + +bool InspIRCd::MatchText(const std::string &sliteral, const std::string &spattern) +{ + return match(sliteral.c_str(),spattern.c_str()); +} + +CmdResult InspIRCd::CallCommandHandler(const std::string &commandname, const char** parameters, int pcnt, userrec* user) +{ + return this->Parser->CallHandler(commandname,parameters,pcnt,user); +} + +bool InspIRCd::IsValidModuleCommand(const std::string &commandname, int pcnt, userrec* user) +{ + return this->Parser->IsValidCommand(commandname, pcnt, user); +} + +void InspIRCd::AddCommand(command_t *f) +{ + if (!this->Parser->CreateCommand(f)) + { + ModuleException err("Command "+std::string(f->command)+" already exists."); + throw (err); + } +} + +void InspIRCd::SendMode(const char** parameters, int pcnt, userrec *user) +{ + this->Modes->Process(parameters,pcnt,user,true); +} + +void InspIRCd::DumpText(userrec* User, const std::string &LinePrefix, stringstream &TextStream) +{ + std::string CompleteLine = LinePrefix; + std::string Word; + while (TextStream >> Word) + { + if (CompleteLine.length() + Word.length() + 3 > 500) + { + User->WriteServ(CompleteLine); + CompleteLine = LinePrefix; + } + CompleteLine = CompleteLine + Word + " "; + } + User->WriteServ(CompleteLine); +} + +userrec* InspIRCd::FindDescriptor(int socket) +{ + return reinterpret_cast<userrec*>(this->SE->GetRef(socket)); +} + +bool InspIRCd::AddMode(ModeHandler* mh, const unsigned char mode) +{ + return this->Modes->AddMode(mh,mode); +} + +bool InspIRCd::AddModeWatcher(ModeWatcher* mw) +{ + return this->Modes->AddModeWatcher(mw); +} + +bool InspIRCd::DelModeWatcher(ModeWatcher* mw) +{ + return this->Modes->DelModeWatcher(mw); +} + +bool InspIRCd::AddResolver(Resolver* r, bool cached) +{ + if (!cached) + return this->Res->AddResolverClass(r); + else + { + r->TriggerCachedResult(); + delete r; + return true; + } +} + +void InspIRCd::AddGLine(long duration, const std::string &source, const std::string &reason, const std::string &hostmask) +{ + XLines->add_gline(duration, source.c_str(), reason.c_str(), hostmask.c_str()); + XLines->apply_lines(APPLY_GLINES); +} + +void InspIRCd::AddQLine(long duration, const std::string &source, const std::string &reason, const std::string &nickname) +{ + XLines->add_qline(duration, source.c_str(), reason.c_str(), nickname.c_str()); + XLines->apply_lines(APPLY_QLINES); +} + +void InspIRCd::AddZLine(long duration, const std::string &source, const std::string &reason, const std::string &ipaddr) +{ + XLines->add_zline(duration, source.c_str(), reason.c_str(), ipaddr.c_str()); + XLines->apply_lines(APPLY_ZLINES); +} + +void InspIRCd::AddKLine(long duration, const std::string &source, const std::string &reason, const std::string &hostmask) +{ + XLines->add_kline(duration, source.c_str(), reason.c_str(), hostmask.c_str()); + XLines->apply_lines(APPLY_KLINES); +} + +void InspIRCd::AddELine(long duration, const std::string &source, const std::string &reason, const std::string &hostmask) +{ + XLines->add_eline(duration, source.c_str(), reason.c_str(), hostmask.c_str()); +} + +bool InspIRCd::DelGLine(const std::string &hostmask) +{ + return XLines->del_gline(hostmask.c_str()); +} + +bool InspIRCd::DelQLine(const std::string &nickname) +{ + return XLines->del_qline(nickname.c_str()); +} + +bool InspIRCd::DelZLine(const std::string &ipaddr) +{ + return XLines->del_zline(ipaddr.c_str()); +} + +bool InspIRCd::DelKLine(const std::string &hostmask) +{ + return XLines->del_kline(hostmask.c_str()); +} + +bool InspIRCd::DelELine(const std::string &hostmask) +{ + return XLines->del_eline(hostmask.c_str()); +} + +/* + * XXX why on *earth* is this in modules.cpp...? I think + * perhaps we need a server.cpp for InspIRCd:: stuff where possible. -- w00t + */ +bool InspIRCd::IsValidMask(const std::string &mask) +{ + char* dest = (char*)mask.c_str(); + if (strchr(dest,'!')==0) + return false; + if (strchr(dest,'@')==0) + return false; + for (char* i = dest; *i; i++) + if (*i < 32) + return false; + for (char* i = dest; *i; i++) + if (*i > 126) + return false; + unsigned int c = 0; + for (char* i = dest; *i; i++) + if (*i == '!') + c++; + if (c>1) + return false; + c = 0; + for (char* i = dest; *i; i++) + if (*i == '@') + c++; + if (c>1) + return false; + + return true; +} + +Module* InspIRCd::FindModule(const std::string &name) +{ + for (int i = 0; i <= this->GetModuleCount(); i++) + { + if (this->Config->module_names[i] == name) + { + return this->modules[i]; + } + } + return NULL; +} + +ConfigReader::ConfigReader(InspIRCd* Instance) : ServerInstance(Instance) +{ + /* Is there any reason to load the entire config file again here? + * it's needed if they specify another config file, but using the + * default one we can just use the global config data - pre-parsed! + */ + this->errorlog = new std::ostringstream(std::stringstream::in | std::stringstream::out); + + this->data = &ServerInstance->Config->config_data; + this->privatehash = false; +} + + +ConfigReader::~ConfigReader() +{ + if (this->errorlog) + DELETE(this->errorlog); + if(this->privatehash) + DELETE(this->data); +} + + +ConfigReader::ConfigReader(InspIRCd* Instance, const std::string &filename) : ServerInstance(Instance) +{ + ServerInstance->Config->ClearStack(); + + this->data = new ConfigDataHash; + this->privatehash = true; + this->errorlog = new std::ostringstream(std::stringstream::in | std::stringstream::out); + this->readerror = ServerInstance->Config->LoadConf(*this->data, filename, *this->errorlog); + if (!this->readerror) + this->error = CONF_FILE_NOT_FOUND; +} + + +std::string ConfigReader::ReadValue(const std::string &tag, const std::string &name, const std::string &default_value, int index, bool allow_linefeeds) +{ + /* Don't need to strlcpy() tag and name anymore, ReadConf() takes const char* */ + std::string result; + + if (!ServerInstance->Config->ConfValue(*this->data, tag, name, default_value, index, result, allow_linefeeds)) + { + this->error = CONF_VALUE_NOT_FOUND; + } + return result; +} + +std::string ConfigReader::ReadValue(const std::string &tag, const std::string &name, int index, bool allow_linefeeds) +{ + return ReadValue(tag, name, "", index, allow_linefeeds); +} + +bool ConfigReader::ReadFlag(const std::string &tag, const std::string &name, const std::string &default_value, int index) +{ + return ServerInstance->Config->ConfValueBool(*this->data, tag, name, default_value, index); +} + +bool ConfigReader::ReadFlag(const std::string &tag, const std::string &name, int index) +{ + return ReadFlag(tag, name, "", index); +} + + +long ConfigReader::ReadInteger(const std::string &tag, const std::string &name, const std::string &default_value, int index, bool needs_unsigned) +{ + int result; + + if(!ServerInstance->Config->ConfValueInteger(*this->data, tag, name, default_value, index, result)) + { + this->error = CONF_VALUE_NOT_FOUND; + return 0; + } + + if ((needs_unsigned) && (result < 0)) + { + this->error = CONF_NOT_UNSIGNED; + return 0; + } + + return result; +} + +long ConfigReader::ReadInteger(const std::string &tag, const std::string &name, int index, bool needs_unsigned) +{ + return ReadInteger(tag, name, "", index, needs_unsigned); +} + +long ConfigReader::GetError() +{ + long olderr = this->error; + this->error = 0; + return olderr; +} + +void ConfigReader::DumpErrors(bool bail, userrec* user) +{ + ServerInstance->Config->ReportConfigError(this->errorlog->str(), bail, user); +} + + +int ConfigReader::Enumerate(const std::string &tag) +{ + return ServerInstance->Config->ConfValueEnum(*this->data, tag); +} + +int ConfigReader::EnumerateValues(const std::string &tag, int index) +{ + return ServerInstance->Config->ConfVarEnum(*this->data, tag, index); +} + +bool ConfigReader::Verify() +{ + return this->readerror; +} + + +FileReader::FileReader(InspIRCd* Instance, const std::string &filename) : ServerInstance(Instance) +{ + LoadFile(filename); +} + +FileReader::FileReader(InspIRCd* Instance) : ServerInstance(Instance) +{ +} + +std::string FileReader::Contents() +{ + std::string x; + for (file_cache::iterator a = this->fc.begin(); a != this->fc.end(); a++) + { + x.append(*a); + x.append("\r\n"); + } + return x; +} + +unsigned long FileReader::ContentSize() +{ + return this->contentsize; +} + +void FileReader::CalcSize() +{ + unsigned long n = 0; + for (file_cache::iterator a = this->fc.begin(); a != this->fc.end(); a++) + n += (a->length() + 2); + this->contentsize = n; +} + +void FileReader::LoadFile(const std::string &filename) +{ + file_cache c; + c.clear(); + if (ServerInstance->Config->ReadFile(c,filename.c_str())) + { + this->fc = c; + this->CalcSize(); + } +} + + +FileReader::~FileReader() +{ +} + +bool FileReader::Exists() +{ + return (!(fc.size() == 0)); +} + +std::string FileReader::GetLine(int x) +{ + if ((x<0) || ((unsigned)x>fc.size())) + return ""; + return fc[x]; +} + +int FileReader::FileSize() +{ + return fc.size(); +} + + diff --git a/src/modules/extra/README b/src/modules/extra/README index 4c4beef9d..7e3096b34 100644 --- a/src/modules/extra/README +++ b/src/modules/extra/README @@ -1 +1,7 @@ -This directory stores modules which require external libraries to compile.
For example, m_filter_pcre requires the PCRE libraries.
To compile any of these modules first ensure you have the required dependencies
(read the online documentation at http://www.inspircd.org/wiki/) and then cp
the .cpp file from this directory into the parent directory (src/modules/) and
re-configure your inspircd with ./configure -update to detect the new module.
\ No newline at end of file +This directory stores modules which require external libraries to compile. +For example, m_filter_pcre requires the PCRE libraries. + +To compile any of these modules first ensure you have the required dependencies +(read the online documentation at http://www.inspircd.org/wiki/) and then cp +the .cpp file from this directory into the parent directory (src/modules/) and +re-configure your inspircd with ./configure -update to detect the new module. diff --git a/src/modules/extra/m_filter_pcre.cpp b/src/modules/extra/m_filter_pcre.cpp index 0c6c05c8c..6fe79a981 100644 --- a/src/modules/extra/m_filter_pcre.cpp +++ b/src/modules/extra/m_filter_pcre.cpp @@ -1 +1,182 @@ -/* +------------------------------------+
* | 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 <pcre.h>
#include "users.h"
#include "channels.h"
#include "modules.h"
#include "m_filter.h"
/* $ModDesc: m_filter with regexps */
/* $CompileFlags: exec("pcre-config --cflags") */
/* $LinkerFlags: exec("pcre-config --libs") rpath("pcre-config --libs") -lpcre */
/* $ModDep: m_filter.h */
#ifdef WINDOWS
#pragma comment(lib, "pcre.lib")
#endif
class PCREFilter : public FilterResult
{
public:
pcre* regexp;
PCREFilter(pcre* r, const std::string &rea, const std::string &act, long gline_time, const std::string &pat, const std::string &flags)
: FilterResult(pat, rea, act, gline_time, flags), regexp(r)
{
}
PCREFilter()
{
}
};
class ModuleFilterPCRE : public FilterBase
{
std::vector<PCREFilter> filters;
pcre *re;
const char *error;
int erroffset;
PCREFilter fr;
public:
ModuleFilterPCRE(InspIRCd* Me)
: FilterBase(Me, "m_filter_pcre.so")
{
OnRehash(NULL,"");
}
virtual ~ModuleFilterPCRE()
{
}
virtual FilterResult* FilterMatch(userrec* user, const std::string &text, int flags)
{
for (std::vector<PCREFilter>::iterator index = filters.begin(); index != filters.end(); index++)
{
/* Skip ones that dont apply to us */
if (!FilterBase::AppliesToMe(user, dynamic_cast<FilterResult*>(&(*index)), flags))
continue;
if (pcre_exec(index->regexp, NULL, text.c_str(), text.length(), 0, 0, NULL, 0) > -1)
{
fr = *index;
if (index != filters.begin())
{
filters.erase(index);
filters.insert(filters.begin(), fr);
}
return &fr;
}
}
return NULL;
}
virtual bool DeleteFilter(const std::string &freeform)
{
for (std::vector<PCREFilter>::iterator i = filters.begin(); i != filters.end(); i++)
{
if (i->freeform == freeform)
{
pcre_free((*i).regexp);
filters.erase(i);
return true;
}
}
return false;
}
virtual void SyncFilters(Module* proto, void* opaque)
{
for (std::vector<PCREFilter>::iterator i = filters.begin(); i != filters.end(); i++)
{
this->SendFilter(proto, opaque, &(*i));
}
}
virtual std::pair<bool, std::string> AddFilter(const std::string &freeform, const std::string &type, const std::string &reason, long duration, const std::string &flags)
{
for (std::vector<PCREFilter>::iterator i = filters.begin(); i != filters.end(); i++)
{
if (i->freeform == freeform)
{
return std::make_pair(false, "Filter already exists");
}
}
re = pcre_compile(freeform.c_str(),0,&error,&erroffset,NULL);
if (!re)
{
ServerInstance->Log(DEFAULT,"Error in regular expression: %s at offset %d: %s\n", freeform.c_str(), erroffset, error);
ServerInstance->Log(DEFAULT,"Regular expression %s not loaded.", freeform.c_str());
return std::make_pair(false, "Error in regular expression at offset " + ConvToStr(erroffset) + ": "+error);
}
else
{
filters.push_back(PCREFilter(re, reason, type, duration, freeform, flags));
return std::make_pair(true, "");
}
}
virtual void OnRehash(userrec* user, const std::string ¶meter)
{
ConfigReader MyConf(ServerInstance);
for (int index = 0; index < MyConf.Enumerate("keyword"); index++)
{
this->DeleteFilter(MyConf.ReadValue("keyword", "pattern", index));
std::string pattern = MyConf.ReadValue("keyword", "pattern", index);
std::string reason = MyConf.ReadValue("keyword", "reason", index);
std::string action = MyConf.ReadValue("keyword", "action", index);
std::string flags = MyConf.ReadValue("keyword", "flags", index);
long gline_time = ServerInstance->Duration(MyConf.ReadValue("keyword", "duration", index));
if (action.empty())
action = "none";
if (flags.empty())
flags = "*";
re = pcre_compile(pattern.c_str(),0,&error,&erroffset,NULL);
if (!re)
{
ServerInstance->Log(DEFAULT,"Error in regular expression: %s at offset %d: %s\n", pattern.c_str(), erroffset, error);
ServerInstance->Log(DEFAULT,"Regular expression %s not loaded.", pattern.c_str());
}
else
{
filters.push_back(PCREFilter(re, reason, action, gline_time, pattern, flags));
ServerInstance->Log(DEFAULT,"Regular expression %s loaded.", pattern.c_str());
}
}
}
virtual int OnStats(char symbol, userrec* user, string_list &results)
{
if (symbol == 's')
{
std::string sn = ServerInstance->Config->ServerName;
for (std::vector<PCREFilter>::iterator i = filters.begin(); i != filters.end(); i++)
{
results.push_back(sn+" 223 "+user->nick+" :REGEXP:"+i->freeform+" "+i->flags+" "+i->action+" "+ConvToStr(i->gline_time)+" :"+i->reason);
}
}
return 0;
}
};
MODULE_INIT(ModuleFilterPCRE);
\ No newline at end of file +/* +------------------------------------+ + * | 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 <pcre.h> +#include "users.h" +#include "channels.h" +#include "modules.h" +#include "m_filter.h" + +/* $ModDesc: m_filter with regexps */ +/* $CompileFlags: exec("pcre-config --cflags") */ +/* $LinkerFlags: exec("pcre-config --libs") rpath("pcre-config --libs") -lpcre */ +/* $ModDep: m_filter.h */ + +#ifdef WINDOWS +#pragma comment(lib, "pcre.lib") +#endif + +class PCREFilter : public FilterResult +{ + public: + pcre* regexp; + + PCREFilter(pcre* r, const std::string &rea, const std::string &act, long gline_time, const std::string &pat, const std::string &flags) + : FilterResult(pat, rea, act, gline_time, flags), regexp(r) + { + } + + PCREFilter() + { + } +}; + +class ModuleFilterPCRE : public FilterBase +{ + std::vector<PCREFilter> filters; + pcre *re; + const char *error; + int erroffset; + PCREFilter fr; + + public: + ModuleFilterPCRE(InspIRCd* Me) + : FilterBase(Me, "m_filter_pcre.so") + { + OnRehash(NULL,""); + } + + virtual ~ModuleFilterPCRE() + { + } + + virtual FilterResult* FilterMatch(userrec* user, const std::string &text, int flags) + { + for (std::vector<PCREFilter>::iterator index = filters.begin(); index != filters.end(); index++) + { + /* Skip ones that dont apply to us */ + + if (!FilterBase::AppliesToMe(user, dynamic_cast<FilterResult*>(&(*index)), flags)) + continue; + + if (pcre_exec(index->regexp, NULL, text.c_str(), text.length(), 0, 0, NULL, 0) > -1) + { + fr = *index; + if (index != filters.begin()) + { + filters.erase(index); + filters.insert(filters.begin(), fr); + } + return &fr; + } + } + return NULL; + } + + virtual bool DeleteFilter(const std::string &freeform) + { + for (std::vector<PCREFilter>::iterator i = filters.begin(); i != filters.end(); i++) + { + if (i->freeform == freeform) + { + pcre_free((*i).regexp); + filters.erase(i); + return true; + } + } + return false; + } + + virtual void SyncFilters(Module* proto, void* opaque) + { + for (std::vector<PCREFilter>::iterator i = filters.begin(); i != filters.end(); i++) + { + this->SendFilter(proto, opaque, &(*i)); + } + } + + virtual std::pair<bool, std::string> AddFilter(const std::string &freeform, const std::string &type, const std::string &reason, long duration, const std::string &flags) + { + for (std::vector<PCREFilter>::iterator i = filters.begin(); i != filters.end(); i++) + { + if (i->freeform == freeform) + { + return std::make_pair(false, "Filter already exists"); + } + } + + re = pcre_compile(freeform.c_str(),0,&error,&erroffset,NULL); + + if (!re) + { + ServerInstance->Log(DEFAULT,"Error in regular expression: %s at offset %d: %s\n", freeform.c_str(), erroffset, error); + ServerInstance->Log(DEFAULT,"Regular expression %s not loaded.", freeform.c_str()); + return std::make_pair(false, "Error in regular expression at offset " + ConvToStr(erroffset) + ": "+error); + } + else + { + filters.push_back(PCREFilter(re, reason, type, duration, freeform, flags)); + return std::make_pair(true, ""); + } + } + + virtual void OnRehash(userrec* user, const std::string ¶meter) + { + ConfigReader MyConf(ServerInstance); + + for (int index = 0; index < MyConf.Enumerate("keyword"); index++) + { + this->DeleteFilter(MyConf.ReadValue("keyword", "pattern", index)); + + std::string pattern = MyConf.ReadValue("keyword", "pattern", index); + std::string reason = MyConf.ReadValue("keyword", "reason", index); + std::string action = MyConf.ReadValue("keyword", "action", index); + std::string flags = MyConf.ReadValue("keyword", "flags", index); + long gline_time = ServerInstance->Duration(MyConf.ReadValue("keyword", "duration", index)); + if (action.empty()) + action = "none"; + if (flags.empty()) + flags = "*"; + + re = pcre_compile(pattern.c_str(),0,&error,&erroffset,NULL); + + if (!re) + { + ServerInstance->Log(DEFAULT,"Error in regular expression: %s at offset %d: %s\n", pattern.c_str(), erroffset, error); + ServerInstance->Log(DEFAULT,"Regular expression %s not loaded.", pattern.c_str()); + } + else + { + filters.push_back(PCREFilter(re, reason, action, gline_time, pattern, flags)); + ServerInstance->Log(DEFAULT,"Regular expression %s loaded.", pattern.c_str()); + } + } + } + + virtual int OnStats(char symbol, userrec* user, string_list &results) + { + if (symbol == 's') + { + std::string sn = ServerInstance->Config->ServerName; + for (std::vector<PCREFilter>::iterator i = filters.begin(); i != filters.end(); i++) + { + results.push_back(sn+" 223 "+user->nick+" :REGEXP:"+i->freeform+" "+i->flags+" "+i->action+" "+ConvToStr(i->gline_time)+" :"+i->reason); + } + } + return 0; + } +}; + +MODULE_INIT(ModuleFilterPCRE); + diff --git a/src/modules/extra/m_httpclienttest.cpp b/src/modules/extra/m_httpclienttest.cpp index 3f74b549b..90e7a5159 100644 --- a/src/modules/extra/m_httpclienttest.cpp +++ b/src/modules/extra/m_httpclienttest.cpp @@ -1 +1,81 @@ -/* +------------------------------------+
* | 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 "users.h"
#include "channels.h"
#include "modules.h"
#include "httpclient.h"
/* $ModDep: httpclient.h */
class MyModule : public Module
{
public:
MyModule(InspIRCd* Me)
: Module::Module(Me)
{
}
virtual ~MyModule()
{
}
virtual void Implements(char* List)
{
List[I_OnRequest] = List[I_OnUserJoin] = List[I_OnUserPart] = 1;
}
virtual Version GetVersion()
{
return Version(1,0,0,1,VF_VENDOR,API_VERSION);
}
virtual void OnUserJoin(userrec* user, chanrec* channel, bool &silent)
{
// method called when a user joins a channel
std::string chan = channel->name;
std::string nick = user->nick;
ServerInstance->Log(DEBUG,"User " + nick + " joined " + chan);
Module* target = ServerInstance->FindModule("m_http_client.so");
if(target)
{
HTTPClientRequest req(ServerInstance, this, target, "http://znc.in/~psychon");
req.Send();
}
else
ServerInstance->Log(DEBUG,"module not found, load it!!");
}
char* OnRequest(Request* req)
{
HTTPClientResponse* resp = (HTTPClientResponse*)req;
if(!strcmp(resp->GetId(), HTTP_CLIENT_RESPONSE))
{
ServerInstance->Log(DEBUG, resp->GetData());
}
return NULL;
}
virtual void OnUserPart(userrec* user, chanrec* channel, const std::string &partmessage, bool &silent)
{
}
};
MODULE_INIT(MyModule);
\ No newline at end of file +/* +------------------------------------+ + * | 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 "users.h" +#include "channels.h" +#include "modules.h" +#include "httpclient.h" + +/* $ModDep: httpclient.h */ + +class MyModule : public Module +{ + +public: + + MyModule(InspIRCd* Me) + : Module::Module(Me) + { + } + + virtual ~MyModule() + { + } + + virtual void Implements(char* List) + { + List[I_OnRequest] = List[I_OnUserJoin] = List[I_OnUserPart] = 1; + } + + virtual Version GetVersion() + { + return Version(1,0,0,1,VF_VENDOR,API_VERSION); + } + + virtual void OnUserJoin(userrec* user, chanrec* channel, bool &silent) + { + // method called when a user joins a channel + + std::string chan = channel->name; + std::string nick = user->nick; + ServerInstance->Log(DEBUG,"User " + nick + " joined " + chan); + + Module* target = ServerInstance->FindModule("m_http_client.so"); + if(target) + { + HTTPClientRequest req(ServerInstance, this, target, "http://znc.in/~psychon"); + req.Send(); + } + else + ServerInstance->Log(DEBUG,"module not found, load it!!"); + } + + char* OnRequest(Request* req) + { + HTTPClientResponse* resp = (HTTPClientResponse*)req; + if(!strcmp(resp->GetId(), HTTP_CLIENT_RESPONSE)) + { + ServerInstance->Log(DEBUG, resp->GetData()); + } + return NULL; + } + + virtual void OnUserPart(userrec* user, chanrec* channel, const std::string &partmessage, bool &silent) + { + } + +}; + +MODULE_INIT(MyModule); + diff --git a/src/modules/extra/m_mysql.cpp b/src/modules/extra/m_mysql.cpp index eeabe5d48..6605bed3c 100644 --- a/src/modules/extra/m_mysql.cpp +++ b/src/modules/extra/m_mysql.cpp @@ -1 +1,889 @@ -/* +------------------------------------+
* | 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 <mysql.h>
#include <pthread.h>
#include "users.h"
#include "channels.h"
#include "modules.h"
#include "m_sqlv2.h"
/* VERSION 2 API: With nonblocking (threaded) requests */
/* $ModDesc: SQL Service Provider module for all other m_sql* modules */
/* $CompileFlags: exec("mysql_config --include") */
/* $LinkerFlags: exec("mysql_config --libs_r") rpath("mysql_config --libs_r") */
/* $ModDep: m_sqlv2.h */
/* THE NONBLOCKING MYSQL API!
*
* MySQL provides no nonblocking (asyncronous) API of its own, and its developers recommend
* that instead, you should thread your program. This is what i've done here to allow for
* asyncronous SQL requests via mysql. The way this works is as follows:
*
* The module spawns a thread via pthreads, and performs its mysql queries in this thread,
* using a queue with priorities. There is a mutex on either end which prevents two threads
* adjusting the queue at the same time, and crashing the ircd. Every 50 milliseconds, the
* worker thread wakes up, and checks if there is a request at the head of its queue.
* If there is, it processes this request, blocking the worker thread but leaving the ircd
* thread to go about its business as usual. During this period, the ircd thread is able
* to insert futher pending requests into the queue.
*
* Once the processing of a request is complete, it is removed from the incoming queue to
* an outgoing queue, and initialized as a 'response'. The worker thread then signals the
* ircd thread (via a loopback socket) of the fact a result is available, by sending the
* connection ID through the connection.
*
* The ircd thread then mutexes the queue once more, reads the outbound response off the head
* of the queue, and sends it on its way to the original calling module.
*
* XXX: You might be asking "why doesnt he just send the response from within the worker thread?"
* The answer to this is simple. The majority of InspIRCd, and in fact most ircd's are not
* threadsafe. This module is designed to be threadsafe and is careful with its use of threads,
* however, if we were to call a module's OnRequest even from within a thread which was not the
* one the module was originally instantiated upon, there is a chance of all hell breaking loose
* if a module is ever put in a re-enterant state (stack corruption could occur, crashes, data
* corruption, and worse, so DONT think about it until the day comes when InspIRCd is 100%
* gauranteed threadsafe!)
*
* For a diagram of this system please see http://www.inspircd.org/wiki/Mysql2
*/
class SQLConnection;
class Notifier;
typedef std::map<std::string, SQLConnection*> ConnMap;
bool giveup = false;
static Module* SQLModule = NULL;
static Notifier* MessagePipe = NULL;
int QueueFD = -1;
#if !defined(MYSQL_VERSION_ID) || MYSQL_VERSION_ID<32224
#define mysql_field_count mysql_num_fields
#endif
typedef std::deque<SQLresult*> ResultQueue;
/* A mutex to wrap around queue accesses */
pthread_mutex_t queue_mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t results_mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t logging_mutex = PTHREAD_MUTEX_INITIALIZER;
/** Represents a mysql result set
*/
class MySQLresult : public SQLresult
{
int currentrow;
std::vector<std::string> colnames;
std::vector<SQLfieldList> fieldlists;
SQLfieldMap* fieldmap;
SQLfieldMap fieldmap2;
SQLfieldList emptyfieldlist;
int rows;
public:
MySQLresult(Module* self, Module* to, MYSQL_RES* res, int affected_rows, unsigned int id) : SQLresult(self, to, id), currentrow(0), fieldmap(NULL)
{
/* A number of affected rows from from mysql_affected_rows.
*/
fieldlists.clear();
rows = 0;
if (affected_rows >= 1)
{
rows = affected_rows;
fieldlists.resize(rows);
}
unsigned int field_count = 0;
if (res)
{
MYSQL_ROW row;
int n = 0;
while ((row = mysql_fetch_row(res)))
{
if (fieldlists.size() < (unsigned int)rows+1)
{
fieldlists.resize(fieldlists.size()+1);
}
field_count = 0;
MYSQL_FIELD *fields = mysql_fetch_fields(res);
if(mysql_num_fields(res) == 0)
break;
if (fields && mysql_num_fields(res))
{
colnames.clear();
while (field_count < mysql_num_fields(res))
{
std::string a = (fields[field_count].name ? fields[field_count].name : "");
std::string b = (row[field_count] ? row[field_count] : "");
SQLfield sqlf(b, !row[field_count]);
colnames.push_back(a);
fieldlists[n].push_back(sqlf);
field_count++;
}
n++;
}
rows++;
}
mysql_free_result(res);
}
}
MySQLresult(Module* self, Module* to, SQLerror e, unsigned int id) : SQLresult(self, to, id), currentrow(0)
{
rows = 0;
error = e;
}
~MySQLresult()
{
}
virtual int Rows()
{
return rows;
}
virtual int Cols()
{
return colnames.size();
}
virtual std::string ColName(int column)
{
if (column < (int)colnames.size())
{
return colnames[column];
}
else
{
throw SQLbadColName();
}
return "";
}
virtual int ColNum(const std::string &column)
{
for (unsigned int i = 0; i < colnames.size(); i++)
{
if (column == colnames[i])
return i;
}
throw SQLbadColName();
return 0;
}
virtual SQLfield GetValue(int row, int column)
{
if ((row >= 0) && (row < rows) && (column >= 0) && (column < Cols()))
{
return fieldlists[row][column];
}
throw SQLbadColName();
/* XXX: We never actually get here because of the throw */
return SQLfield("",true);
}
virtual SQLfieldList& GetRow()
{
if (currentrow < rows)
return fieldlists[currentrow];
else
return emptyfieldlist;
}
virtual SQLfieldMap& GetRowMap()
{
fieldmap2.clear();
if (currentrow < rows)
{
for (int i = 0; i < Cols(); i++)
{
fieldmap2.insert(std::make_pair(colnames[i],GetValue(currentrow, i)));
}
currentrow++;
}
return fieldmap2;
}
virtual SQLfieldList* GetRowPtr()
{
SQLfieldList* fieldlist = new SQLfieldList();
if (currentrow < rows)
{
for (int i = 0; i < Rows(); i++)
{
fieldlist->push_back(fieldlists[currentrow][i]);
}
currentrow++;
}
return fieldlist;
}
virtual SQLfieldMap* GetRowMapPtr()
{
fieldmap = new SQLfieldMap();
if (currentrow < rows)
{
for (int i = 0; i < Cols(); i++)
{
fieldmap->insert(std::make_pair(colnames[i],GetValue(currentrow, i)));
}
currentrow++;
}
return fieldmap;
}
virtual void Free(SQLfieldMap* fm)
{
delete fm;
}
virtual void Free(SQLfieldList* fl)
{
delete fl;
}
};
class SQLConnection;
void NotifyMainThread(SQLConnection* connection_with_new_result);
/** Represents a connection to a mysql database
*/
class SQLConnection : public classbase
{
protected:
MYSQL connection;
MYSQL_RES *res;
MYSQL_ROW row;
SQLhost host;
std::map<std::string,std::string> thisrow;
bool Enabled;
public:
QueryQueue queue;
ResultQueue rq;
// This constructor creates an SQLConnection object with the given credentials, but does not connect yet.
SQLConnection(const SQLhost &hi) : host(hi), Enabled(false)
{
}
~SQLConnection()
{
Close();
}
// This method connects to the database using the credentials supplied to the constructor, and returns
// true upon success.
bool Connect()
{
unsigned int timeout = 1;
mysql_init(&connection);
mysql_options(&connection,MYSQL_OPT_CONNECT_TIMEOUT,(char*)&timeout);
return mysql_real_connect(&connection, host.host.c_str(), host.user.c_str(), host.pass.c_str(), host.name.c_str(), host.port, NULL, 0);
}
void DoLeadingQuery()
{
if (!CheckConnection())
return;
/* Parse the command string and dispatch it to mysql */
SQLrequest& req = queue.front();
/* Pointer to the buffer we screw around with substitution in */
char* query;
/* Pointer to the current end of query, where we append new stuff */
char* queryend;
/* Total length of the unescaped parameters */
unsigned long paramlen;
/* Total length of query, used for binary-safety in mysql_real_query */
unsigned long querylength = 0;
paramlen = 0;
for(ParamL::iterator i = req.query.p.begin(); i != req.query.p.end(); i++)
{
paramlen += i->size();
}
/* To avoid a lot of allocations, allocate enough memory for the biggest the escaped query could possibly be.
* sizeofquery + (totalparamlength*2) + 1
*
* The +1 is for null-terminating the string for mysql_real_escape_string
*/
query = new char[req.query.q.length() + (paramlen*2) + 1];
queryend = query;
/* Okay, now we have a buffer large enough we need to start copying the query into it and escaping and substituting
* the parameters into it...
*/
for(unsigned long i = 0; i < req.query.q.length(); i++)
{
if(req.query.q[i] == '?')
{
/* We found a place to substitute..what fun.
* use mysql calls to escape and write the
* escaped string onto the end of our query buffer,
* then we "just" need to make sure queryend is
* pointing at the right place.
*/
if(req.query.p.size())
{
unsigned long len = mysql_real_escape_string(&connection, queryend, req.query.p.front().c_str(), req.query.p.front().length());
queryend += len;
req.query.p.pop_front();
}
else
break;
}
else
{
*queryend = req.query.q[i];
queryend++;
}
querylength++;
}
*queryend = 0;
pthread_mutex_lock(&queue_mutex);
req.query.q = query;
pthread_mutex_unlock(&queue_mutex);
if (!mysql_real_query(&connection, req.query.q.data(), req.query.q.length()))
{
/* Successfull query */
res = mysql_use_result(&connection);
unsigned long rows = mysql_affected_rows(&connection);
MySQLresult* r = new MySQLresult(SQLModule, req.GetSource(), res, rows, req.id);
r->dbid = this->GetID();
r->query = req.query.q;
/* Put this new result onto the results queue.
* XXX: Remember to mutex the queue!
*/
pthread_mutex_lock(&results_mutex);
rq.push_back(r);
pthread_mutex_unlock(&results_mutex);
}
else
{
/* XXX: See /usr/include/mysql/mysqld_error.h for a list of
* possible error numbers and error messages */
SQLerror e(QREPLY_FAIL, ConvToStr(mysql_errno(&connection)) + std::string(": ") + mysql_error(&connection));
MySQLresult* r = new MySQLresult(SQLModule, req.GetSource(), e, req.id);
r->dbid = this->GetID();
r->query = req.query.q;
pthread_mutex_lock(&results_mutex);
rq.push_back(r);
pthread_mutex_unlock(&results_mutex);
}
/* Now signal the main thread that we've got a result to process.
* Pass them this connection id as what to examine
*/
delete[] query;
NotifyMainThread(this);
}
bool ConnectionLost()
{
if (&connection) {
return (mysql_ping(&connection) != 0);
}
else return false;
}
bool CheckConnection()
{
if (ConnectionLost()) {
return Connect();
}
else return true;
}
std::string GetError()
{
return mysql_error(&connection);
}
const std::string& GetID()
{
return host.id;
}
std::string GetHost()
{
return host.host;
}
void SetEnable(bool Enable)
{
Enabled = Enable;
}
bool IsEnabled()
{
return Enabled;
}
void Close()
{
mysql_close(&connection);
}
const SQLhost& GetConfHost()
{
return host;
}
};
ConnMap Connections;
bool HasHost(const SQLhost &host)
{
for (ConnMap::iterator iter = Connections.begin(); iter != Connections.end(); iter++)
{
if (host == iter->second->GetConfHost())
return true;
}
return false;
}
bool HostInConf(ConfigReader* conf, const SQLhost &h)
{
for(int i = 0; i < conf->Enumerate("database"); i++)
{
SQLhost host;
host.id = conf->ReadValue("database", "id", i);
host.host = conf->ReadValue("database", "hostname", i);
host.port = conf->ReadInteger("database", "port", i, true);
host.name = conf->ReadValue("database", "name", i);
host.user = conf->ReadValue("database", "username", i);
host.pass = conf->ReadValue("database", "password", i);
host.ssl = conf->ReadFlag("database", "ssl", i);
if (h == host)
return true;
}
return false;
}
void ClearOldConnections(ConfigReader* conf)
{
ConnMap::iterator i,safei;
for (i = Connections.begin(); i != Connections.end(); i++)
{
if (!HostInConf(conf, i->second->GetConfHost()))
{
DELETE(i->second);
safei = i;
--i;
Connections.erase(safei);
}
}
}
void ClearAllConnections()
{
ConnMap::iterator i;
while ((i = Connections.begin()) != Connections.end())
{
Connections.erase(i);
DELETE(i->second);
}
}
void ConnectDatabases(InspIRCd* ServerInstance)
{
for (ConnMap::iterator i = Connections.begin(); i != Connections.end(); i++)
{
if (i->second->IsEnabled())
continue;
i->second->SetEnable(true);
if (!i->second->Connect())
{
/* XXX: MUTEX */
pthread_mutex_lock(&logging_mutex);
ServerInstance->Log(DEFAULT,"SQL: Failed to connect database "+i->second->GetHost()+": Error: "+i->second->GetError());
i->second->SetEnable(false);
pthread_mutex_unlock(&logging_mutex);
}
}
}
void LoadDatabases(ConfigReader* conf, InspIRCd* ServerInstance)
{
ClearOldConnections(conf);
for (int j =0; j < conf->Enumerate("database"); j++)
{
SQLhost host;
host.id = conf->ReadValue("database", "id", j);
host.host = conf->ReadValue("database", "hostname", j);
host.port = conf->ReadInteger("database", "port", j, true);
host.name = conf->ReadValue("database", "name", j);
host.user = conf->ReadValue("database", "username", j);
host.pass = conf->ReadValue("database", "password", j);
host.ssl = conf->ReadFlag("database", "ssl", j);
if (HasHost(host))
continue;
if (!host.id.empty() && !host.host.empty() && !host.name.empty() && !host.user.empty() && !host.pass.empty())
{
SQLConnection* ThisSQL = new SQLConnection(host);
Connections[host.id] = ThisSQL;
}
}
ConnectDatabases(ServerInstance);
}
char FindCharId(const std::string &id)
{
char i = 1;
for (ConnMap::iterator iter = Connections.begin(); iter != Connections.end(); ++iter, ++i)
{
if (iter->first == id)
{
return i;
}
}
return 0;
}
ConnMap::iterator GetCharId(char id)
{
char i = 1;
for (ConnMap::iterator iter = Connections.begin(); iter != Connections.end(); ++iter, ++i)
{
if (i == id)
return iter;
}
return Connections.end();
}
void NotifyMainThread(SQLConnection* connection_with_new_result)
{
/* Here we write() to the socket the main thread has open
* and we connect()ed back to before our thread became active.
* The main thread is using a nonblocking socket tied into
* the socket engine, so they wont block and they'll receive
* nearly instant notification. Because we're in a seperate
* thread, we can just use standard connect(), and we can
* block if we like. We just send the connection id of the
* connection back.
*
* NOTE: We only send a single char down the connection, this
* way we know it wont get a partial read at the other end if
* the system is especially congested (see bug #263).
* The function FindCharId translates a connection name into a
* one character id, and GetCharId translates a character id
* back into an iterator.
*/
char id = FindCharId(connection_with_new_result->GetID());
send(QueueFD, &id, 1, 0);
}
void* DispatcherThread(void* arg);
/** Used by m_mysql to notify one thread when the other has a result
*/
class Notifier : public InspSocket
{
insp_sockaddr sock_us;
socklen_t uslen;
public:
/* Create a socket on a random port. Let the tcp stack allocate us an available port */
#ifdef IPV6
Notifier(InspIRCd* SI) : InspSocket(SI, "::1", 0, true, 3000)
#else
Notifier(InspIRCd* SI) : InspSocket(SI, "127.0.0.1", 0, true, 3000)
#endif
{
uslen = sizeof(sock_us);
if (getsockname(this->fd,(sockaddr*)&sock_us,&uslen))
{
throw ModuleException("Could not create random listening port on localhost");
}
}
Notifier(InspIRCd* SI, int newfd, char* ip) : InspSocket(SI, newfd, ip)
{
}
/* Using getsockname and ntohs, we can determine which port number we were allocated */
int GetPort()
{
#ifdef IPV6
return ntohs(sock_us.sin6_port);
#else
return ntohs(sock_us.sin_port);
#endif
}
virtual int OnIncomingConnection(int newsock, char* ip)
{
Notifier* n = new Notifier(this->Instance, newsock, ip);
n = n; /* Stop bitching at me, GCC */
return true;
}
virtual bool OnDataReady()
{
char data = 0;
/* NOTE: Only a single character is read so we know we
* cant get a partial read. (We've been told that theres
* data waiting, so we wont ever get EAGAIN)
* The function GetCharId translates a single character
* back into an iterator.
*/
if (read(this->GetFd(), &data, 1) > 0)
{
ConnMap::iterator iter = GetCharId(data);
if (iter != Connections.end())
{
/* Lock the mutex, send back the data */
pthread_mutex_lock(&results_mutex);
ResultQueue::iterator n = iter->second->rq.begin();
(*n)->Send();
iter->second->rq.pop_front();
pthread_mutex_unlock(&results_mutex);
return true;
}
/* No error, but unknown id */
return true;
}
/* Erk, error on descriptor! */
return false;
}
};
/** MySQL module
*/
class ModuleSQL : public Module
{
public:
ConfigReader *Conf;
InspIRCd* PublicServerInstance;
pthread_t Dispatcher;
int currid;
bool rehashing;
ModuleSQL(InspIRCd* Me)
: Module::Module(Me), rehashing(false)
{
ServerInstance->UseInterface("SQLutils");
Conf = new ConfigReader(ServerInstance);
PublicServerInstance = ServerInstance;
currid = 0;
SQLModule = this;
MessagePipe = new Notifier(ServerInstance);
pthread_attr_t attribs;
pthread_attr_init(&attribs);
pthread_attr_setdetachstate(&attribs, PTHREAD_CREATE_DETACHED);
if (pthread_create(&this->Dispatcher, &attribs, DispatcherThread, (void *)this) != 0)
{
throw ModuleException("m_mysql: Failed to create dispatcher thread: " + std::string(strerror(errno)));
}
if (!ServerInstance->PublishFeature("SQL", this))
{
/* Tell worker thread to exit NOW */
giveup = true;
throw ModuleException("m_mysql: Unable to publish feature 'SQL'");
}
ServerInstance->PublishInterface("SQL", this);
}
virtual ~ModuleSQL()
{
giveup = true;
ClearAllConnections();
DELETE(Conf);
ServerInstance->UnpublishInterface("SQL", this);
ServerInstance->UnpublishFeature("SQL");
ServerInstance->DoneWithInterface("SQLutils");
}
void Implements(char* List)
{
List[I_OnRehash] = List[I_OnRequest] = 1;
}
unsigned long NewID()
{
if (currid+1 == 0)
currid++;
return ++currid;
}
char* OnRequest(Request* request)
{
if(strcmp(SQLREQID, request->GetId()) == 0)
{
SQLrequest* req = (SQLrequest*)request;
/* XXX: Lock */
pthread_mutex_lock(&queue_mutex);
ConnMap::iterator iter;
char* returnval = NULL;
if((iter = Connections.find(req->dbid)) != Connections.end())
{
req->id = NewID();
iter->second->queue.push(*req);
returnval = SQLSUCCESS;
}
else
{
req->error.Id(BAD_DBID);
}
pthread_mutex_unlock(&queue_mutex);
/* XXX: Unlock */
return returnval;
}
return NULL;
}
virtual void OnRehash(userrec* user, const std::string ¶meter)
{
rehashing = true;
}
virtual Version GetVersion()
{
return Version(1,1,0,0,VF_VENDOR|VF_SERVICEPROVIDER,API_VERSION);
}
};
void* DispatcherThread(void* arg)
{
ModuleSQL* thismodule = (ModuleSQL*)arg;
LoadDatabases(thismodule->Conf, thismodule->PublicServerInstance);
/* Connect back to the Notifier */
if ((QueueFD = socket(AF_FAMILY, SOCK_STREAM, 0)) == -1)
{
/* crap, we're out of sockets... */
return NULL;
}
insp_sockaddr addr;
#ifdef IPV6
insp_aton("::1", &addr.sin6_addr);
addr.sin6_family = AF_FAMILY;
addr.sin6_port = htons(MessagePipe->GetPort());
#else
insp_inaddr ia;
insp_aton("127.0.0.1", &ia);
addr.sin_family = AF_FAMILY;
addr.sin_addr = ia;
addr.sin_port = htons(MessagePipe->GetPort());
#endif
if (connect(QueueFD, (sockaddr*)&addr,sizeof(addr)) == -1)
{
/* wtf, we cant connect to it, but we just created it! */
return NULL;
}
while (!giveup)
{
if (thismodule->rehashing)
{
/* XXX: Lock */
pthread_mutex_lock(&queue_mutex);
thismodule->rehashing = false;
LoadDatabases(thismodule->Conf, thismodule->PublicServerInstance);
pthread_mutex_unlock(&queue_mutex);
/* XXX: Unlock */
}
SQLConnection* conn = NULL;
/* XXX: Lock here for safety */
pthread_mutex_lock(&queue_mutex);
for (ConnMap::iterator i = Connections.begin(); i != Connections.end(); i++)
{
if (i->second->queue.totalsize())
{
conn = i->second;
break;
}
}
pthread_mutex_unlock(&queue_mutex);
/* XXX: Unlock */
/* Theres an item! */
if (conn)
{
conn->DoLeadingQuery();
/* XXX: Lock */
pthread_mutex_lock(&queue_mutex);
conn->queue.pop();
pthread_mutex_unlock(&queue_mutex);
/* XXX: Unlock */
}
usleep(50);
}
return NULL;
}
MODULE_INIT(ModuleSQL);
\ No newline at end of file +/* +------------------------------------+ + * | 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 <mysql.h> +#include <pthread.h> +#include "users.h" +#include "channels.h" +#include "modules.h" +#include "m_sqlv2.h" + +/* VERSION 2 API: With nonblocking (threaded) requests */ + +/* $ModDesc: SQL Service Provider module for all other m_sql* modules */ +/* $CompileFlags: exec("mysql_config --include") */ +/* $LinkerFlags: exec("mysql_config --libs_r") rpath("mysql_config --libs_r") */ +/* $ModDep: m_sqlv2.h */ + +/* THE NONBLOCKING MYSQL API! + * + * MySQL provides no nonblocking (asyncronous) API of its own, and its developers recommend + * that instead, you should thread your program. This is what i've done here to allow for + * asyncronous SQL requests via mysql. The way this works is as follows: + * + * The module spawns a thread via pthreads, and performs its mysql queries in this thread, + * using a queue with priorities. There is a mutex on either end which prevents two threads + * adjusting the queue at the same time, and crashing the ircd. Every 50 milliseconds, the + * worker thread wakes up, and checks if there is a request at the head of its queue. + * If there is, it processes this request, blocking the worker thread but leaving the ircd + * thread to go about its business as usual. During this period, the ircd thread is able + * to insert futher pending requests into the queue. + * + * Once the processing of a request is complete, it is removed from the incoming queue to + * an outgoing queue, and initialized as a 'response'. The worker thread then signals the + * ircd thread (via a loopback socket) of the fact a result is available, by sending the + * connection ID through the connection. + * + * The ircd thread then mutexes the queue once more, reads the outbound response off the head + * of the queue, and sends it on its way to the original calling module. + * + * XXX: You might be asking "why doesnt he just send the response from within the worker thread?" + * The answer to this is simple. The majority of InspIRCd, and in fact most ircd's are not + * threadsafe. This module is designed to be threadsafe and is careful with its use of threads, + * however, if we were to call a module's OnRequest even from within a thread which was not the + * one the module was originally instantiated upon, there is a chance of all hell breaking loose + * if a module is ever put in a re-enterant state (stack corruption could occur, crashes, data + * corruption, and worse, so DONT think about it until the day comes when InspIRCd is 100% + * gauranteed threadsafe!) + * + * For a diagram of this system please see http://www.inspircd.org/wiki/Mysql2 + */ + + +class SQLConnection; +class Notifier; + + +typedef std::map<std::string, SQLConnection*> ConnMap; +bool giveup = false; +static Module* SQLModule = NULL; +static Notifier* MessagePipe = NULL; +int QueueFD = -1; + + +#if !defined(MYSQL_VERSION_ID) || MYSQL_VERSION_ID<32224 +#define mysql_field_count mysql_num_fields +#endif + +typedef std::deque<SQLresult*> ResultQueue; + +/* A mutex to wrap around queue accesses */ +pthread_mutex_t queue_mutex = PTHREAD_MUTEX_INITIALIZER; + +pthread_mutex_t results_mutex = PTHREAD_MUTEX_INITIALIZER; + +pthread_mutex_t logging_mutex = PTHREAD_MUTEX_INITIALIZER; + +/** Represents a mysql result set + */ +class MySQLresult : public SQLresult +{ + int currentrow; + std::vector<std::string> colnames; + std::vector<SQLfieldList> fieldlists; + SQLfieldMap* fieldmap; + SQLfieldMap fieldmap2; + SQLfieldList emptyfieldlist; + int rows; + public: + + MySQLresult(Module* self, Module* to, MYSQL_RES* res, int affected_rows, unsigned int id) : SQLresult(self, to, id), currentrow(0), fieldmap(NULL) + { + /* A number of affected rows from from mysql_affected_rows. + */ + fieldlists.clear(); + rows = 0; + if (affected_rows >= 1) + { + rows = affected_rows; + fieldlists.resize(rows); + } + unsigned int field_count = 0; + if (res) + { + MYSQL_ROW row; + int n = 0; + while ((row = mysql_fetch_row(res))) + { + if (fieldlists.size() < (unsigned int)rows+1) + { + fieldlists.resize(fieldlists.size()+1); + } + field_count = 0; + MYSQL_FIELD *fields = mysql_fetch_fields(res); + if(mysql_num_fields(res) == 0) + break; + if (fields && mysql_num_fields(res)) + { + colnames.clear(); + while (field_count < mysql_num_fields(res)) + { + std::string a = (fields[field_count].name ? fields[field_count].name : ""); + std::string b = (row[field_count] ? row[field_count] : ""); + SQLfield sqlf(b, !row[field_count]); + colnames.push_back(a); + fieldlists[n].push_back(sqlf); + field_count++; + } + n++; + } + rows++; + } + mysql_free_result(res); + } + } + + MySQLresult(Module* self, Module* to, SQLerror e, unsigned int id) : SQLresult(self, to, id), currentrow(0) + { + rows = 0; + error = e; + } + + ~MySQLresult() + { + } + + virtual int Rows() + { + return rows; + } + + virtual int Cols() + { + return colnames.size(); + } + + virtual std::string ColName(int column) + { + if (column < (int)colnames.size()) + { + return colnames[column]; + } + else + { + throw SQLbadColName(); + } + return ""; + } + + virtual int ColNum(const std::string &column) + { + for (unsigned int i = 0; i < colnames.size(); i++) + { + if (column == colnames[i]) + return i; + } + throw SQLbadColName(); + return 0; + } + + virtual SQLfield GetValue(int row, int column) + { + if ((row >= 0) && (row < rows) && (column >= 0) && (column < Cols())) + { + return fieldlists[row][column]; + } + + throw SQLbadColName(); + + /* XXX: We never actually get here because of the throw */ + return SQLfield("",true); + } + + virtual SQLfieldList& GetRow() + { + if (currentrow < rows) + return fieldlists[currentrow]; + else + return emptyfieldlist; + } + + virtual SQLfieldMap& GetRowMap() + { + fieldmap2.clear(); + + if (currentrow < rows) + { + for (int i = 0; i < Cols(); i++) + { + fieldmap2.insert(std::make_pair(colnames[i],GetValue(currentrow, i))); + } + currentrow++; + } + + return fieldmap2; + } + + virtual SQLfieldList* GetRowPtr() + { + SQLfieldList* fieldlist = new SQLfieldList(); + + if (currentrow < rows) + { + for (int i = 0; i < Rows(); i++) + { + fieldlist->push_back(fieldlists[currentrow][i]); + } + currentrow++; + } + return fieldlist; + } + + virtual SQLfieldMap* GetRowMapPtr() + { + fieldmap = new SQLfieldMap(); + + if (currentrow < rows) + { + for (int i = 0; i < Cols(); i++) + { + fieldmap->insert(std::make_pair(colnames[i],GetValue(currentrow, i))); + } + currentrow++; + } + + return fieldmap; + } + + virtual void Free(SQLfieldMap* fm) + { + delete fm; + } + + virtual void Free(SQLfieldList* fl) + { + delete fl; + } +}; + +class SQLConnection; + +void NotifyMainThread(SQLConnection* connection_with_new_result); + +/** Represents a connection to a mysql database + */ +class SQLConnection : public classbase +{ + protected: + + MYSQL connection; + MYSQL_RES *res; + MYSQL_ROW row; + SQLhost host; + std::map<std::string,std::string> thisrow; + bool Enabled; + + public: + + QueryQueue queue; + ResultQueue rq; + + // This constructor creates an SQLConnection object with the given credentials, but does not connect yet. + SQLConnection(const SQLhost &hi) : host(hi), Enabled(false) + { + } + + ~SQLConnection() + { + Close(); + } + + // This method connects to the database using the credentials supplied to the constructor, and returns + // true upon success. + bool Connect() + { + unsigned int timeout = 1; + mysql_init(&connection); + mysql_options(&connection,MYSQL_OPT_CONNECT_TIMEOUT,(char*)&timeout); + return mysql_real_connect(&connection, host.host.c_str(), host.user.c_str(), host.pass.c_str(), host.name.c_str(), host.port, NULL, 0); + } + + void DoLeadingQuery() + { + if (!CheckConnection()) + return; + + /* Parse the command string and dispatch it to mysql */ + SQLrequest& req = queue.front(); + + /* Pointer to the buffer we screw around with substitution in */ + char* query; + + /* Pointer to the current end of query, where we append new stuff */ + char* queryend; + + /* Total length of the unescaped parameters */ + unsigned long paramlen; + + /* Total length of query, used for binary-safety in mysql_real_query */ + unsigned long querylength = 0; + + paramlen = 0; + + for(ParamL::iterator i = req.query.p.begin(); i != req.query.p.end(); i++) + { + paramlen += i->size(); + } + + /* To avoid a lot of allocations, allocate enough memory for the biggest the escaped query could possibly be. + * sizeofquery + (totalparamlength*2) + 1 + * + * The +1 is for null-terminating the string for mysql_real_escape_string + */ + + query = new char[req.query.q.length() + (paramlen*2) + 1]; + queryend = query; + + /* Okay, now we have a buffer large enough we need to start copying the query into it and escaping and substituting + * the parameters into it... + */ + + for(unsigned long i = 0; i < req.query.q.length(); i++) + { + if(req.query.q[i] == '?') + { + /* We found a place to substitute..what fun. + * use mysql calls to escape and write the + * escaped string onto the end of our query buffer, + * then we "just" need to make sure queryend is + * pointing at the right place. + */ + if(req.query.p.size()) + { + unsigned long len = mysql_real_escape_string(&connection, queryend, req.query.p.front().c_str(), req.query.p.front().length()); + + queryend += len; + req.query.p.pop_front(); + } + else + break; + } + else + { + *queryend = req.query.q[i]; + queryend++; + } + querylength++; + } + + *queryend = 0; + + pthread_mutex_lock(&queue_mutex); + req.query.q = query; + pthread_mutex_unlock(&queue_mutex); + + if (!mysql_real_query(&connection, req.query.q.data(), req.query.q.length())) + { + /* Successfull query */ + res = mysql_use_result(&connection); + unsigned long rows = mysql_affected_rows(&connection); + MySQLresult* r = new MySQLresult(SQLModule, req.GetSource(), res, rows, req.id); + r->dbid = this->GetID(); + r->query = req.query.q; + /* Put this new result onto the results queue. + * XXX: Remember to mutex the queue! + */ + pthread_mutex_lock(&results_mutex); + rq.push_back(r); + pthread_mutex_unlock(&results_mutex); + } + else + { + /* XXX: See /usr/include/mysql/mysqld_error.h for a list of + * possible error numbers and error messages */ + SQLerror e(QREPLY_FAIL, ConvToStr(mysql_errno(&connection)) + std::string(": ") + mysql_error(&connection)); + MySQLresult* r = new MySQLresult(SQLModule, req.GetSource(), e, req.id); + r->dbid = this->GetID(); + r->query = req.query.q; + + pthread_mutex_lock(&results_mutex); + rq.push_back(r); + pthread_mutex_unlock(&results_mutex); + } + + /* Now signal the main thread that we've got a result to process. + * Pass them this connection id as what to examine + */ + + delete[] query; + + NotifyMainThread(this); + } + + bool ConnectionLost() + { + if (&connection) { + return (mysql_ping(&connection) != 0); + } + else return false; + } + + bool CheckConnection() + { + if (ConnectionLost()) { + return Connect(); + } + else return true; + } + + std::string GetError() + { + return mysql_error(&connection); + } + + const std::string& GetID() + { + return host.id; + } + + std::string GetHost() + { + return host.host; + } + + void SetEnable(bool Enable) + { + Enabled = Enable; + } + + bool IsEnabled() + { + return Enabled; + } + + void Close() + { + mysql_close(&connection); + } + + const SQLhost& GetConfHost() + { + return host; + } + +}; + +ConnMap Connections; + +bool HasHost(const SQLhost &host) +{ + for (ConnMap::iterator iter = Connections.begin(); iter != Connections.end(); iter++) + { + if (host == iter->second->GetConfHost()) + return true; + } + return false; +} + +bool HostInConf(ConfigReader* conf, const SQLhost &h) +{ + for(int i = 0; i < conf->Enumerate("database"); i++) + { + SQLhost host; + host.id = conf->ReadValue("database", "id", i); + host.host = conf->ReadValue("database", "hostname", i); + host.port = conf->ReadInteger("database", "port", i, true); + host.name = conf->ReadValue("database", "name", i); + host.user = conf->ReadValue("database", "username", i); + host.pass = conf->ReadValue("database", "password", i); + host.ssl = conf->ReadFlag("database", "ssl", i); + if (h == host) + return true; + } + return false; +} + +void ClearOldConnections(ConfigReader* conf) +{ + ConnMap::iterator i,safei; + for (i = Connections.begin(); i != Connections.end(); i++) + { + if (!HostInConf(conf, i->second->GetConfHost())) + { + DELETE(i->second); + safei = i; + --i; + Connections.erase(safei); + } + } +} + +void ClearAllConnections() +{ + ConnMap::iterator i; + while ((i = Connections.begin()) != Connections.end()) + { + Connections.erase(i); + DELETE(i->second); + } +} + +void ConnectDatabases(InspIRCd* ServerInstance) +{ + for (ConnMap::iterator i = Connections.begin(); i != Connections.end(); i++) + { + if (i->second->IsEnabled()) + continue; + + i->second->SetEnable(true); + if (!i->second->Connect()) + { + /* XXX: MUTEX */ + pthread_mutex_lock(&logging_mutex); + ServerInstance->Log(DEFAULT,"SQL: Failed to connect database "+i->second->GetHost()+": Error: "+i->second->GetError()); + i->second->SetEnable(false); + pthread_mutex_unlock(&logging_mutex); + } + } +} + +void LoadDatabases(ConfigReader* conf, InspIRCd* ServerInstance) +{ + ClearOldConnections(conf); + for (int j =0; j < conf->Enumerate("database"); j++) + { + SQLhost host; + host.id = conf->ReadValue("database", "id", j); + host.host = conf->ReadValue("database", "hostname", j); + host.port = conf->ReadInteger("database", "port", j, true); + host.name = conf->ReadValue("database", "name", j); + host.user = conf->ReadValue("database", "username", j); + host.pass = conf->ReadValue("database", "password", j); + host.ssl = conf->ReadFlag("database", "ssl", j); + + if (HasHost(host)) + continue; + + if (!host.id.empty() && !host.host.empty() && !host.name.empty() && !host.user.empty() && !host.pass.empty()) + { + SQLConnection* ThisSQL = new SQLConnection(host); + Connections[host.id] = ThisSQL; + } + } + ConnectDatabases(ServerInstance); +} + +char FindCharId(const std::string &id) +{ + char i = 1; + for (ConnMap::iterator iter = Connections.begin(); iter != Connections.end(); ++iter, ++i) + { + if (iter->first == id) + { + return i; + } + } + return 0; +} + +ConnMap::iterator GetCharId(char id) +{ + char i = 1; + for (ConnMap::iterator iter = Connections.begin(); iter != Connections.end(); ++iter, ++i) + { + if (i == id) + return iter; + } + return Connections.end(); +} + +void NotifyMainThread(SQLConnection* connection_with_new_result) +{ + /* Here we write() to the socket the main thread has open + * and we connect()ed back to before our thread became active. + * The main thread is using a nonblocking socket tied into + * the socket engine, so they wont block and they'll receive + * nearly instant notification. Because we're in a seperate + * thread, we can just use standard connect(), and we can + * block if we like. We just send the connection id of the + * connection back. + * + * NOTE: We only send a single char down the connection, this + * way we know it wont get a partial read at the other end if + * the system is especially congested (see bug #263). + * The function FindCharId translates a connection name into a + * one character id, and GetCharId translates a character id + * back into an iterator. + */ + char id = FindCharId(connection_with_new_result->GetID()); + send(QueueFD, &id, 1, 0); +} + +void* DispatcherThread(void* arg); + +/** Used by m_mysql to notify one thread when the other has a result + */ +class Notifier : public InspSocket +{ + insp_sockaddr sock_us; + socklen_t uslen; + + + public: + + /* Create a socket on a random port. Let the tcp stack allocate us an available port */ +#ifdef IPV6 + Notifier(InspIRCd* SI) : InspSocket(SI, "::1", 0, true, 3000) +#else + Notifier(InspIRCd* SI) : InspSocket(SI, "127.0.0.1", 0, true, 3000) +#endif + { + uslen = sizeof(sock_us); + if (getsockname(this->fd,(sockaddr*)&sock_us,&uslen)) + { + throw ModuleException("Could not create random listening port on localhost"); + } + } + + Notifier(InspIRCd* SI, int newfd, char* ip) : InspSocket(SI, newfd, ip) + { + } + + /* Using getsockname and ntohs, we can determine which port number we were allocated */ + int GetPort() + { +#ifdef IPV6 + return ntohs(sock_us.sin6_port); +#else + return ntohs(sock_us.sin_port); +#endif + } + + virtual int OnIncomingConnection(int newsock, char* ip) + { + Notifier* n = new Notifier(this->Instance, newsock, ip); + n = n; /* Stop bitching at me, GCC */ + return true; + } + + virtual bool OnDataReady() + { + char data = 0; + /* NOTE: Only a single character is read so we know we + * cant get a partial read. (We've been told that theres + * data waiting, so we wont ever get EAGAIN) + * The function GetCharId translates a single character + * back into an iterator. + */ + if (read(this->GetFd(), &data, 1) > 0) + { + ConnMap::iterator iter = GetCharId(data); + if (iter != Connections.end()) + { + /* Lock the mutex, send back the data */ + pthread_mutex_lock(&results_mutex); + ResultQueue::iterator n = iter->second->rq.begin(); + (*n)->Send(); + iter->second->rq.pop_front(); + pthread_mutex_unlock(&results_mutex); + return true; + } + /* No error, but unknown id */ + return true; + } + + /* Erk, error on descriptor! */ + return false; + } +}; + +/** MySQL module + */ +class ModuleSQL : public Module +{ + public: + + ConfigReader *Conf; + InspIRCd* PublicServerInstance; + pthread_t Dispatcher; + int currid; + bool rehashing; + + ModuleSQL(InspIRCd* Me) + : Module::Module(Me), rehashing(false) + { + ServerInstance->UseInterface("SQLutils"); + + Conf = new ConfigReader(ServerInstance); + PublicServerInstance = ServerInstance; + currid = 0; + SQLModule = this; + + MessagePipe = new Notifier(ServerInstance); + + pthread_attr_t attribs; + pthread_attr_init(&attribs); + pthread_attr_setdetachstate(&attribs, PTHREAD_CREATE_DETACHED); + if (pthread_create(&this->Dispatcher, &attribs, DispatcherThread, (void *)this) != 0) + { + throw ModuleException("m_mysql: Failed to create dispatcher thread: " + std::string(strerror(errno))); + } + + if (!ServerInstance->PublishFeature("SQL", this)) + { + /* Tell worker thread to exit NOW */ + giveup = true; + throw ModuleException("m_mysql: Unable to publish feature 'SQL'"); + } + + ServerInstance->PublishInterface("SQL", this); + } + + virtual ~ModuleSQL() + { + giveup = true; + ClearAllConnections(); + DELETE(Conf); + ServerInstance->UnpublishInterface("SQL", this); + ServerInstance->UnpublishFeature("SQL"); + ServerInstance->DoneWithInterface("SQLutils"); + } + + + void Implements(char* List) + { + List[I_OnRehash] = List[I_OnRequest] = 1; + } + + unsigned long NewID() + { + if (currid+1 == 0) + currid++; + return ++currid; + } + + char* OnRequest(Request* request) + { + if(strcmp(SQLREQID, request->GetId()) == 0) + { + SQLrequest* req = (SQLrequest*)request; + + /* XXX: Lock */ + pthread_mutex_lock(&queue_mutex); + + ConnMap::iterator iter; + + char* returnval = NULL; + + if((iter = Connections.find(req->dbid)) != Connections.end()) + { + req->id = NewID(); + iter->second->queue.push(*req); + returnval = SQLSUCCESS; + } + else + { + req->error.Id(BAD_DBID); + } + + pthread_mutex_unlock(&queue_mutex); + /* XXX: Unlock */ + + return returnval; + } + + return NULL; + } + + virtual void OnRehash(userrec* user, const std::string ¶meter) + { + rehashing = true; + } + + virtual Version GetVersion() + { + return Version(1,1,0,0,VF_VENDOR|VF_SERVICEPROVIDER,API_VERSION); + } + +}; + +void* DispatcherThread(void* arg) +{ + ModuleSQL* thismodule = (ModuleSQL*)arg; + LoadDatabases(thismodule->Conf, thismodule->PublicServerInstance); + + /* Connect back to the Notifier */ + + if ((QueueFD = socket(AF_FAMILY, SOCK_STREAM, 0)) == -1) + { + /* crap, we're out of sockets... */ + return NULL; + } + + insp_sockaddr addr; + +#ifdef IPV6 + insp_aton("::1", &addr.sin6_addr); + addr.sin6_family = AF_FAMILY; + addr.sin6_port = htons(MessagePipe->GetPort()); +#else + insp_inaddr ia; + insp_aton("127.0.0.1", &ia); + addr.sin_family = AF_FAMILY; + addr.sin_addr = ia; + addr.sin_port = htons(MessagePipe->GetPort()); +#endif + + if (connect(QueueFD, (sockaddr*)&addr,sizeof(addr)) == -1) + { + /* wtf, we cant connect to it, but we just created it! */ + return NULL; + } + + while (!giveup) + { + if (thismodule->rehashing) + { + /* XXX: Lock */ + pthread_mutex_lock(&queue_mutex); + thismodule->rehashing = false; + LoadDatabases(thismodule->Conf, thismodule->PublicServerInstance); + pthread_mutex_unlock(&queue_mutex); + /* XXX: Unlock */ + } + + SQLConnection* conn = NULL; + /* XXX: Lock here for safety */ + pthread_mutex_lock(&queue_mutex); + for (ConnMap::iterator i = Connections.begin(); i != Connections.end(); i++) + { + if (i->second->queue.totalsize()) + { + conn = i->second; + break; + } + } + pthread_mutex_unlock(&queue_mutex); + /* XXX: Unlock */ + + /* Theres an item! */ + if (conn) + { + conn->DoLeadingQuery(); + + /* XXX: Lock */ + pthread_mutex_lock(&queue_mutex); + conn->queue.pop(); + pthread_mutex_unlock(&queue_mutex); + /* XXX: Unlock */ + } + + usleep(50); + } + + return NULL; +} + +MODULE_INIT(ModuleSQL); + diff --git a/src/modules/extra/m_pgsql.cpp b/src/modules/extra/m_pgsql.cpp index 9e85a40de..5d267fc1a 100644 --- a/src/modules/extra/m_pgsql.cpp +++ b/src/modules/extra/m_pgsql.cpp @@ -1 +1,984 @@ -/* +------------------------------------+
* | 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 <cstdlib>
#include <sstream>
#include <libpq-fe.h>
#include "users.h"
#include "channels.h"
#include "modules.h"
#include "configreader.h"
#include "m_sqlv2.h"
/* $ModDesc: PostgreSQL Service Provider module for all other m_sql* modules, uses v2 of the SQL API */
/* $CompileFlags: -Iexec("pg_config --includedir") eval("my $s = `pg_config --version`;$s =~ /^.*?(\d+)\.(\d+)\.(\d+).*?$/;my $v = hex(sprintf("0x%02x%02x%02x", $1, $2, $3));print "-DPGSQL_HAS_ESCAPECONN" if(($v >= 0x080104) || ($v >= 0x07030F && $v < 0x070400) || ($v >= 0x07040D && $v < 0x080000) || ($v >= 0x080008 && $v < 0x080100));") */
/* $LinkerFlags: -Lexec("pg_config --libdir") -lpq */
/* $ModDep: m_sqlv2.h */
/* SQLConn rewritten by peavey to
* use EventHandler instead of
* InspSocket. This is much neater
* and gives total control of destroy
* and delete of resources.
*/
/* Forward declare, so we can have the typedef neatly at the top */
class SQLConn;
typedef std::map<std::string, SQLConn*> ConnMap;
/* CREAD, Connecting and wants read event
* CWRITE, Connecting and wants write event
* WREAD, Connected/Working and wants read event
* WWRITE, Connected/Working and wants write event
* RREAD, Resetting and wants read event
* RWRITE, Resetting and wants write event
*/
enum SQLstatus { CREAD, CWRITE, WREAD, WWRITE, RREAD, RWRITE };
/** SQLhost::GetDSN() - Overload to return correct DSN for PostgreSQL
*/
std::string SQLhost::GetDSN()
{
std::ostringstream conninfo("connect_timeout = '2'");
if (ip.length())
conninfo << " hostaddr = '" << ip << "'";
if (port)
conninfo << " port = '" << port << "'";
if (name.length())
conninfo << " dbname = '" << name << "'";
if (user.length())
conninfo << " user = '" << user << "'";
if (pass.length())
conninfo << " password = '" << pass << "'";
if (ssl)
{
conninfo << " sslmode = 'require'";
}
else
{
conninfo << " sslmode = 'disable'";
}
return conninfo.str();
}
class ReconnectTimer : public InspTimer
{
private:
Module* mod;
public:
ReconnectTimer(InspIRCd* SI, Module* m)
: InspTimer(5, SI->Time(), false), mod(m)
{
}
virtual void Tick(time_t TIME);
};
/** Used to resolve sql server hostnames
*/
class SQLresolver : public Resolver
{
private:
SQLhost host;
Module* mod;
public:
SQLresolver(Module* m, InspIRCd* Instance, const SQLhost& hi, bool &cached)
: Resolver(Instance, hi.host, DNS_QUERY_FORWARD, cached, (Module*)m), host(hi), mod(m)
{
}
virtual void OnLookupComplete(const std::string &result, unsigned int ttl, bool cached);
virtual void OnError(ResolverError e, const std::string &errormessage)
{
ServerInstance->Log(DEBUG, "PgSQL: DNS lookup failed (%s), dying horribly", errormessage.c_str());
}
};
/** PgSQLresult is a subclass of the mostly-pure-virtual class SQLresult.
* All SQL providers must create their own subclass and define it's methods using that
* database library's data retriveal functions. The aim is to avoid a slow and inefficient process
* of converting all data to a common format before it reaches the result structure. This way
* data is passes to the module nearly as directly as if it was using the API directly itself.
*/
class PgSQLresult : public SQLresult
{
PGresult* res;
int currentrow;
int rows;
int cols;
SQLfieldList* fieldlist;
SQLfieldMap* fieldmap;
public:
PgSQLresult(Module* self, Module* to, unsigned long id, PGresult* result)
: SQLresult(self, to, id), res(result), currentrow(0), fieldlist(NULL), fieldmap(NULL)
{
rows = PQntuples(res);
cols = PQnfields(res);
}
~PgSQLresult()
{
/* If we allocated these, free them... */
if(fieldlist)
DELETE(fieldlist);
if(fieldmap)
DELETE(fieldmap);
PQclear(res);
}
virtual int Rows()
{
if(!cols && !rows)
{
return atoi(PQcmdTuples(res));
}
else
{
return rows;
}
}
virtual int Cols()
{
return PQnfields(res);
}
virtual std::string ColName(int column)
{
char* name = PQfname(res, column);
return (name) ? name : "";
}
virtual int ColNum(const std::string &column)
{
int n = PQfnumber(res, column.c_str());
if(n == -1)
{
throw SQLbadColName();
}
else
{
return n;
}
}
virtual SQLfield GetValue(int row, int column)
{
char* v = PQgetvalue(res, row, column);
if(v)
{
return SQLfield(std::string(v, PQgetlength(res, row, column)), PQgetisnull(res, row, column));
}
else
{
throw SQLbadColName();
}
}
virtual SQLfieldList& GetRow()
{
/* In an effort to reduce overhead we don't actually allocate the list
* until the first time it's needed...so...
*/
if(fieldlist)
{
fieldlist->clear();
}
else
{
fieldlist = new SQLfieldList;
}
if(currentrow < PQntuples(res))
{
int cols = PQnfields(res);
for(int i = 0; i < cols; i++)
{
fieldlist->push_back(GetValue(currentrow, i));
}
currentrow++;
}
return *fieldlist;
}
virtual SQLfieldMap& GetRowMap()
{
/* In an effort to reduce overhead we don't actually allocate the map
* until the first time it's needed...so...
*/
if(fieldmap)
{
fieldmap->clear();
}
else
{
fieldmap = new SQLfieldMap;
}
if(currentrow < PQntuples(res))
{
int cols = PQnfields(res);
for(int i = 0; i < cols; i++)
{
fieldmap->insert(std::make_pair(ColName(i), GetValue(currentrow, i)));
}
currentrow++;
}
return *fieldmap;
}
virtual SQLfieldList* GetRowPtr()
{
SQLfieldList* fl = new SQLfieldList;
if(currentrow < PQntuples(res))
{
int cols = PQnfields(res);
for(int i = 0; i < cols; i++)
{
fl->push_back(GetValue(currentrow, i));
}
currentrow++;
}
return fl;
}
virtual SQLfieldMap* GetRowMapPtr()
{
SQLfieldMap* fm = new SQLfieldMap;
if(currentrow < PQntuples(res))
{
int cols = PQnfields(res);
for(int i = 0; i < cols; i++)
{
fm->insert(std::make_pair(ColName(i), GetValue(currentrow, i)));
}
currentrow++;
}
return fm;
}
virtual void Free(SQLfieldMap* fm)
{
DELETE(fm);
}
virtual void Free(SQLfieldList* fl)
{
DELETE(fl);
}
};
/** SQLConn represents one SQL session.
*/
class SQLConn : public EventHandler
{
private:
InspIRCd* Instance;
SQLhost confhost; /* The <database> entry */
Module* us; /* Pointer to the SQL provider itself */
PGconn* sql; /* PgSQL database connection handle */
SQLstatus status; /* PgSQL database connection status */
bool qinprog; /* If there is currently a query in progress */
QueryQueue queue; /* Queue of queries waiting to be executed on this connection */
time_t idle; /* Time we last heard from the database */
public:
SQLConn(InspIRCd* SI, Module* self, const SQLhost& hi)
: EventHandler(), Instance(SI), confhost(hi), us(self), sql(NULL), status(CWRITE), qinprog(false)
{
idle = this->Instance->Time();
if(!DoConnect())
{
Instance->Log(DEFAULT, "WARNING: Could not connect to database with id: " + ConvToStr(hi.id));
DelayReconnect();
}
}
~SQLConn()
{
Close();
}
virtual void HandleEvent(EventType et, int errornum)
{
switch (et)
{
case EVENT_READ:
OnDataReady();
break;
case EVENT_WRITE:
OnWriteReady();
break;
case EVENT_ERROR:
DelayReconnect();
break;
default:
break;
}
}
bool DoConnect()
{
if(!(sql = PQconnectStart(confhost.GetDSN().c_str())))
return false;
if(PQstatus(sql) == CONNECTION_BAD)
return false;
if(PQsetnonblocking(sql, 1) == -1)
return false;
/* OK, we've initalised the connection, now to get it hooked into the socket engine
* and then start polling it.
*/
this->fd = PQsocket(sql);
if(this->fd <= -1)
return false;
if (!this->Instance->SE->AddFd(this))
{
Instance->Log(DEBUG, "BUG: Couldn't add pgsql socket to socket engine");
return false;
}
/* Socket all hooked into the engine, now to tell PgSQL to start connecting */
return DoPoll();
}
bool DoPoll()
{
switch(PQconnectPoll(sql))
{
case PGRES_POLLING_WRITING:
Instance->SE->WantWrite(this);
status = CWRITE;
return true;
case PGRES_POLLING_READING:
status = CREAD;
return true;
case PGRES_POLLING_FAILED:
return false;
case PGRES_POLLING_OK:
status = WWRITE;
return DoConnectedPoll();
default:
return true;
}
}
bool DoConnectedPoll()
{
if(!qinprog && queue.totalsize())
{
/* There's no query currently in progress, and there's queries in the queue. */
SQLrequest& query = queue.front();
DoQuery(query);
}
if(PQconsumeInput(sql))
{
/* We just read stuff from the server, that counts as it being alive
* so update the idle-since time :p
*/
idle = this->Instance->Time();
if (PQisBusy(sql))
{
/* Nothing happens here */
}
else if (qinprog)
{
/* Grab the request we're processing */
SQLrequest& query = queue.front();
/* Get a pointer to the module we're about to return the result to */
Module* to = query.GetSource();
/* Fetch the result.. */
PGresult* result = PQgetResult(sql);
/* PgSQL would allow a query string to be sent which has multiple
* queries in it, this isn't portable across database backends and
* we don't want modules doing it. But just in case we make sure we
* drain any results there are and just use the last one.
* If the module devs are behaving there will only be one result.
*/
while (PGresult* temp = PQgetResult(sql))
{
PQclear(result);
result = temp;
}
if(to)
{
/* ..and the result */
PgSQLresult reply(us, to, query.id, result);
/* Fix by brain, make sure the original query gets sent back in the reply */
reply.query = query.query.q;
switch(PQresultStatus(result))
{
case PGRES_EMPTY_QUERY:
case PGRES_BAD_RESPONSE:
case PGRES_FATAL_ERROR:
reply.error.Id(QREPLY_FAIL);
reply.error.Str(PQresultErrorMessage(result));
default:;
/* No action, other values are not errors */
}
reply.Send();
/* PgSQLresult's destructor will free the PGresult */
}
else
{
/* If the client module is unloaded partway through a query then the provider will set
* the pointer to NULL. We cannot just cancel the query as the result will still come
* through at some point...and it could get messy if we play with invalid pointers...
*/
PQclear(result);
}
qinprog = false;
queue.pop();
DoConnectedPoll();
}
return true;
}
else
{
/* I think we'll assume this means the server died...it might not,
* but I think that any error serious enough we actually get here
* deserves to reconnect [/excuse]
* Returning true so the core doesn't try and close the connection.
*/
DelayReconnect();
return true;
}
}
bool DoResetPoll()
{
switch(PQresetPoll(sql))
{
case PGRES_POLLING_WRITING:
Instance->SE->WantWrite(this);
status = CWRITE;
return DoPoll();
case PGRES_POLLING_READING:
status = CREAD;
return true;
case PGRES_POLLING_FAILED:
return false;
case PGRES_POLLING_OK:
status = WWRITE;
return DoConnectedPoll();
default:
return true;
}
}
bool OnDataReady()
{
/* Always return true here, false would close the socket - we need to do that ourselves with the pgsql API */
return DoEvent();
}
bool OnWriteReady()
{
/* Always return true here, false would close the socket - we need to do that ourselves with the pgsql API */
return DoEvent();
}
bool OnConnected()
{
return DoEvent();
}
void DelayReconnect();
bool DoEvent()
{
bool ret;
if((status == CREAD) || (status == CWRITE))
{
ret = DoPoll();
}
else if((status == RREAD) || (status == RWRITE))
{
ret = DoResetPoll();
}
else
{
ret = DoConnectedPoll();
}
return ret;
}
SQLerror DoQuery(SQLrequest &req)
{
if((status == WREAD) || (status == WWRITE))
{
if(!qinprog)
{
/* Parse the command string and dispatch it */
/* Pointer to the buffer we screw around with substitution in */
char* query;
/* Pointer to the current end of query, where we append new stuff */
char* queryend;
/* Total length of the unescaped parameters */
unsigned int paramlen;
paramlen = 0;
for(ParamL::iterator i = req.query.p.begin(); i != req.query.p.end(); i++)
{
paramlen += i->size();
}
/* To avoid a lot of allocations, allocate enough memory for the biggest the escaped query could possibly be.
* sizeofquery + (totalparamlength*2) + 1
*
* The +1 is for null-terminating the string for PQsendQuery()
*/
query = new char[req.query.q.length() + (paramlen*2) + 1];
queryend = query;
/* Okay, now we have a buffer large enough we need to start copying the query into it and escaping and substituting
* the parameters into it...
*/
for(unsigned int i = 0; i < req.query.q.length(); i++)
{
if(req.query.q[i] == '?')
{
/* We found a place to substitute..what fun.
* Use the PgSQL calls to escape and write the
* escaped string onto the end of our query buffer,
* then we "just" need to make sure queryend is
* pointing at the right place.
*/
if(req.query.p.size())
{
int error = 0;
size_t len = 0;
#ifdef PGSQL_HAS_ESCAPECONN
len = PQescapeStringConn(sql, queryend, req.query.p.front().c_str(), req.query.p.front().length(), &error);
#else
len = PQescapeString (queryend, req.query.p.front().c_str(), req.query.p.front().length());
#endif
if(error)
{
Instance->Log(DEBUG, "BUG: Apparently PQescapeStringConn() failed somehow...don't know how or what to do...");
}
/* Incremenet queryend to the end of the newly escaped parameter */
queryend += len;
/* Remove the parameter we just substituted in */
req.query.p.pop_front();
}
else
{
Instance->Log(DEBUG, "BUG: Found a substitution location but no parameter to substitute :|");
break;
}
}
else
{
*queryend = req.query.q[i];
queryend++;
}
}
/* Null-terminate the query */
*queryend = 0;
req.query.q = query;
if(PQsendQuery(sql, query))
{
qinprog = true;
delete[] query;
return SQLerror();
}
else
{
delete[] query;
return SQLerror(QSEND_FAIL, PQerrorMessage(sql));
}
}
}
return SQLerror(BAD_CONN, "Can't query until connection is complete");
}
SQLerror Query(const SQLrequest &req)
{
queue.push(req);
if(!qinprog && queue.totalsize())
{
/* There's no query currently in progress, and there's queries in the queue. */
SQLrequest& query = queue.front();
return DoQuery(query);
}
else
{
return SQLerror();
}
}
void OnUnloadModule(Module* mod)
{
queue.PurgeModule(mod);
}
const SQLhost GetConfHost()
{
return confhost;
}
void Close() {
if (!this->Instance->SE->DelFd(this))
{
if (sql && PQstatus(sql) == CONNECTION_BAD)
{
this->Instance->SE->DelFd(this, true);
}
else
{
Instance->Log(DEBUG, "BUG: PQsocket cant be removed from socket engine!");
}
}
if(sql)
{
PQfinish(sql);
sql = NULL;
}
}
};
class ModulePgSQL : public Module
{
private:
ConnMap connections;
unsigned long currid;
char* sqlsuccess;
ReconnectTimer* retimer;
public:
ModulePgSQL(InspIRCd* Me)
: Module::Module(Me), currid(0)
{
ServerInstance->UseInterface("SQLutils");
sqlsuccess = new char[strlen(SQLSUCCESS)+1];
strlcpy(sqlsuccess, SQLSUCCESS, strlen(SQLSUCCESS));
if (!ServerInstance->PublishFeature("SQL", this))
{
throw ModuleException("BUG: PgSQL Unable to publish feature 'SQL'");
}
ReadConf();
ServerInstance->PublishInterface("SQL", this);
}
virtual ~ModulePgSQL()
{
if (retimer)
ServerInstance->Timers->DelTimer(retimer);
ClearAllConnections();
delete[] sqlsuccess;
ServerInstance->UnpublishInterface("SQL", this);
ServerInstance->UnpublishFeature("SQL");
ServerInstance->DoneWithInterface("SQLutils");
}
void Implements(char* List)
{
List[I_OnUnloadModule] = List[I_OnRequest] = List[I_OnRehash] = List[I_OnUserRegister] = List[I_OnCheckReady] = List[I_OnUserDisconnect] = 1;
}
virtual void OnRehash(userrec* user, const std::string ¶meter)
{
ReadConf();
}
bool HasHost(const SQLhost &host)
{
for (ConnMap::iterator iter = connections.begin(); iter != connections.end(); iter++)
{
if (host == iter->second->GetConfHost())
return true;
}
return false;
}
bool HostInConf(const SQLhost &h)
{
ConfigReader conf(ServerInstance);
for(int i = 0; i < conf.Enumerate("database"); i++)
{
SQLhost host;
host.id = conf.ReadValue("database", "id", i);
host.host = conf.ReadValue("database", "hostname", i);
host.port = conf.ReadInteger("database", "port", i, true);
host.name = conf.ReadValue("database", "name", i);
host.user = conf.ReadValue("database", "username", i);
host.pass = conf.ReadValue("database", "password", i);
host.ssl = conf.ReadFlag("database", "ssl", "0", i);
if (h == host)
return true;
}
return false;
}
void ReadConf()
{
ClearOldConnections();
ConfigReader conf(ServerInstance);
for(int i = 0; i < conf.Enumerate("database"); i++)
{
SQLhost host;
int ipvalid;
host.id = conf.ReadValue("database", "id", i);
host.host = conf.ReadValue("database", "hostname", i);
host.port = conf.ReadInteger("database", "port", i, true);
host.name = conf.ReadValue("database", "name", i);
host.user = conf.ReadValue("database", "username", i);
host.pass = conf.ReadValue("database", "password", i);
host.ssl = conf.ReadFlag("database", "ssl", "0", i);
if (HasHost(host))
continue;
#ifdef IPV6
if (strchr(host.host.c_str(),':'))
{
in6_addr blargle;
ipvalid = inet_pton(AF_INET6, host.host.c_str(), &blargle);
}
else
#endif
{
in_addr blargle;
ipvalid = inet_aton(host.host.c_str(), &blargle);
}
if(ipvalid > 0)
{
/* The conversion succeeded, we were given an IP and we can give it straight to SQLConn */
host.ip = host.host;
this->AddConn(host);
}
else if(ipvalid == 0)
{
/* Conversion failed, assume it's a host */
SQLresolver* resolver;
try
{
bool cached;
resolver = new SQLresolver(this, ServerInstance, host, cached);
ServerInstance->AddResolver(resolver, cached);
}
catch(...)
{
/* THE WORLD IS COMING TO AN END! */
}
}
else
{
/* Invalid address family, die horribly. */
ServerInstance->Log(DEBUG, "BUG: insp_aton failed returning -1, oh noes.");
}
}
}
void ClearOldConnections()
{
ConnMap::iterator iter,safei;
for (iter = connections.begin(); iter != connections.end(); iter++)
{
if (!HostInConf(iter->second->GetConfHost()))
{
DELETE(iter->second);
safei = iter;
--iter;
connections.erase(safei);
}
}
}
void ClearAllConnections()
{
ConnMap::iterator i;
while ((i = connections.begin()) != connections.end())
{
connections.erase(i);
DELETE(i->second);
}
}
void AddConn(const SQLhost& hi)
{
if (HasHost(hi))
{
ServerInstance->Log(DEFAULT, "WARNING: A pgsql connection with id: %s already exists, possibly due to DNS delay. Aborting connection attempt.", hi.id.c_str());
return;
}
SQLConn* newconn;
/* The conversion succeeded, we were given an IP and we can give it straight to SQLConn */
newconn = new SQLConn(ServerInstance, this, hi);
connections.insert(std::make_pair(hi.id, newconn));
}
void ReconnectConn(SQLConn* conn)
{
for (ConnMap::iterator iter = connections.begin(); iter != connections.end(); iter++)
{
if (conn == iter->second)
{
DELETE(iter->second);
connections.erase(iter);
break;
}
}
retimer = new ReconnectTimer(ServerInstance, this);
ServerInstance->Timers->AddTimer(retimer);
}
virtual char* OnRequest(Request* request)
{
if(strcmp(SQLREQID, request->GetId()) == 0)
{
SQLrequest* req = (SQLrequest*)request;
ConnMap::iterator iter;
if((iter = connections.find(req->dbid)) != connections.end())
{
/* Execute query */
req->id = NewID();
req->error = iter->second->Query(*req);
return (req->error.Id() == NO_ERROR) ? sqlsuccess : NULL;
}
else
{
req->error.Id(BAD_DBID);
return NULL;
}
}
return NULL;
}
virtual void OnUnloadModule(Module* mod, const std::string& name)
{
/* When a module unloads we have to check all the pending queries for all our connections
* and set the Module* specifying where the query came from to NULL. If the query has already
* been dispatched then when it is processed it will be dropped if the pointer is NULL.
*
* If the queries we find are not already being executed then we can simply remove them immediately.
*/
for(ConnMap::iterator iter = connections.begin(); iter != connections.end(); iter++)
{
iter->second->OnUnloadModule(mod);
}
}
unsigned long NewID()
{
if (currid+1 == 0)
currid++;
return ++currid;
}
virtual Version GetVersion()
{
return Version(1, 1, 0, 0, VF_VENDOR|VF_SERVICEPROVIDER, API_VERSION);
}
};
/* move this here to use AddConn, rather that than having the whole
* module above SQLConn, since this is buggin me right now :/
*/
void SQLresolver::OnLookupComplete(const std::string &result, unsigned int ttl, bool cached)
{
host.ip = result;
((ModulePgSQL*)mod)->AddConn(host);
((ModulePgSQL*)mod)->ClearOldConnections();
}
void ReconnectTimer::Tick(time_t time)
{
((ModulePgSQL*)mod)->ReadConf();
}
void SQLConn::DelayReconnect()
{
((ModulePgSQL*)us)->ReconnectConn(this);
}
MODULE_INIT(ModulePgSQL);
\ No newline at end of file +/* +------------------------------------+ + * | 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 <cstdlib> +#include <sstream> +#include <libpq-fe.h> +#include "users.h" +#include "channels.h" +#include "modules.h" +#include "configreader.h" +#include "m_sqlv2.h" + +/* $ModDesc: PostgreSQL Service Provider module for all other m_sql* modules, uses v2 of the SQL API */ +/* $CompileFlags: -Iexec("pg_config --includedir") eval("my $s = `pg_config --version`;$s =~ /^.*?(\d+)\.(\d+)\.(\d+).*?$/;my $v = hex(sprintf("0x%02x%02x%02x", $1, $2, $3));print "-DPGSQL_HAS_ESCAPECONN" if(($v >= 0x080104) || ($v >= 0x07030F && $v < 0x070400) || ($v >= 0x07040D && $v < 0x080000) || ($v >= 0x080008 && $v < 0x080100));") */ +/* $LinkerFlags: -Lexec("pg_config --libdir") -lpq */ +/* $ModDep: m_sqlv2.h */ + + +/* SQLConn rewritten by peavey to + * use EventHandler instead of + * InspSocket. This is much neater + * and gives total control of destroy + * and delete of resources. + */ + +/* Forward declare, so we can have the typedef neatly at the top */ +class SQLConn; + +typedef std::map<std::string, SQLConn*> ConnMap; + +/* CREAD, Connecting and wants read event + * CWRITE, Connecting and wants write event + * WREAD, Connected/Working and wants read event + * WWRITE, Connected/Working and wants write event + * RREAD, Resetting and wants read event + * RWRITE, Resetting and wants write event + */ +enum SQLstatus { CREAD, CWRITE, WREAD, WWRITE, RREAD, RWRITE }; + +/** SQLhost::GetDSN() - Overload to return correct DSN for PostgreSQL + */ +std::string SQLhost::GetDSN() +{ + std::ostringstream conninfo("connect_timeout = '2'"); + + if (ip.length()) + conninfo << " hostaddr = '" << ip << "'"; + + if (port) + conninfo << " port = '" << port << "'"; + + if (name.length()) + conninfo << " dbname = '" << name << "'"; + + if (user.length()) + conninfo << " user = '" << user << "'"; + + if (pass.length()) + conninfo << " password = '" << pass << "'"; + + if (ssl) + { + conninfo << " sslmode = 'require'"; + } + else + { + conninfo << " sslmode = 'disable'"; + } + + return conninfo.str(); +} + +class ReconnectTimer : public InspTimer +{ + private: + Module* mod; + public: + ReconnectTimer(InspIRCd* SI, Module* m) + : InspTimer(5, SI->Time(), false), mod(m) + { + } + virtual void Tick(time_t TIME); +}; + + +/** Used to resolve sql server hostnames + */ +class SQLresolver : public Resolver +{ + private: + SQLhost host; + Module* mod; + public: + SQLresolver(Module* m, InspIRCd* Instance, const SQLhost& hi, bool &cached) + : Resolver(Instance, hi.host, DNS_QUERY_FORWARD, cached, (Module*)m), host(hi), mod(m) + { + } + + virtual void OnLookupComplete(const std::string &result, unsigned int ttl, bool cached); + + virtual void OnError(ResolverError e, const std::string &errormessage) + { + ServerInstance->Log(DEBUG, "PgSQL: DNS lookup failed (%s), dying horribly", errormessage.c_str()); + } +}; + +/** PgSQLresult is a subclass of the mostly-pure-virtual class SQLresult. + * All SQL providers must create their own subclass and define it's methods using that + * database library's data retriveal functions. The aim is to avoid a slow and inefficient process + * of converting all data to a common format before it reaches the result structure. This way + * data is passes to the module nearly as directly as if it was using the API directly itself. + */ + +class PgSQLresult : public SQLresult +{ + PGresult* res; + int currentrow; + int rows; + int cols; + + SQLfieldList* fieldlist; + SQLfieldMap* fieldmap; +public: + PgSQLresult(Module* self, Module* to, unsigned long id, PGresult* result) + : SQLresult(self, to, id), res(result), currentrow(0), fieldlist(NULL), fieldmap(NULL) + { + rows = PQntuples(res); + cols = PQnfields(res); + } + + ~PgSQLresult() + { + /* If we allocated these, free them... */ + if(fieldlist) + DELETE(fieldlist); + + if(fieldmap) + DELETE(fieldmap); + + PQclear(res); + } + + virtual int Rows() + { + if(!cols && !rows) + { + return atoi(PQcmdTuples(res)); + } + else + { + return rows; + } + } + + virtual int Cols() + { + return PQnfields(res); + } + + virtual std::string ColName(int column) + { + char* name = PQfname(res, column); + + return (name) ? name : ""; + } + + virtual int ColNum(const std::string &column) + { + int n = PQfnumber(res, column.c_str()); + + if(n == -1) + { + throw SQLbadColName(); + } + else + { + return n; + } + } + + virtual SQLfield GetValue(int row, int column) + { + char* v = PQgetvalue(res, row, column); + + if(v) + { + return SQLfield(std::string(v, PQgetlength(res, row, column)), PQgetisnull(res, row, column)); + } + else + { + throw SQLbadColName(); + } + } + + virtual SQLfieldList& GetRow() + { + /* In an effort to reduce overhead we don't actually allocate the list + * until the first time it's needed...so... + */ + if(fieldlist) + { + fieldlist->clear(); + } + else + { + fieldlist = new SQLfieldList; + } + + if(currentrow < PQntuples(res)) + { + int cols = PQnfields(res); + + for(int i = 0; i < cols; i++) + { + fieldlist->push_back(GetValue(currentrow, i)); + } + + currentrow++; + } + + return *fieldlist; + } + + virtual SQLfieldMap& GetRowMap() + { + /* In an effort to reduce overhead we don't actually allocate the map + * until the first time it's needed...so... + */ + if(fieldmap) + { + fieldmap->clear(); + } + else + { + fieldmap = new SQLfieldMap; + } + + if(currentrow < PQntuples(res)) + { + int cols = PQnfields(res); + + for(int i = 0; i < cols; i++) + { + fieldmap->insert(std::make_pair(ColName(i), GetValue(currentrow, i))); + } + + currentrow++; + } + + return *fieldmap; + } + + virtual SQLfieldList* GetRowPtr() + { + SQLfieldList* fl = new SQLfieldList; + + if(currentrow < PQntuples(res)) + { + int cols = PQnfields(res); + + for(int i = 0; i < cols; i++) + { + fl->push_back(GetValue(currentrow, i)); + } + + currentrow++; + } + + return fl; + } + + virtual SQLfieldMap* GetRowMapPtr() + { + SQLfieldMap* fm = new SQLfieldMap; + + if(currentrow < PQntuples(res)) + { + int cols = PQnfields(res); + + for(int i = 0; i < cols; i++) + { + fm->insert(std::make_pair(ColName(i), GetValue(currentrow, i))); + } + + currentrow++; + } + + return fm; + } + + virtual void Free(SQLfieldMap* fm) + { + DELETE(fm); + } + + virtual void Free(SQLfieldList* fl) + { + DELETE(fl); + } +}; + +/** SQLConn represents one SQL session. + */ +class SQLConn : public EventHandler +{ + private: + InspIRCd* Instance; + SQLhost confhost; /* The <database> entry */ + Module* us; /* Pointer to the SQL provider itself */ + PGconn* sql; /* PgSQL database connection handle */ + SQLstatus status; /* PgSQL database connection status */ + bool qinprog; /* If there is currently a query in progress */ + QueryQueue queue; /* Queue of queries waiting to be executed on this connection */ + time_t idle; /* Time we last heard from the database */ + + public: + SQLConn(InspIRCd* SI, Module* self, const SQLhost& hi) + : EventHandler(), Instance(SI), confhost(hi), us(self), sql(NULL), status(CWRITE), qinprog(false) + { + idle = this->Instance->Time(); + if(!DoConnect()) + { + Instance->Log(DEFAULT, "WARNING: Could not connect to database with id: " + ConvToStr(hi.id)); + DelayReconnect(); + } + } + + ~SQLConn() + { + Close(); + } + + virtual void HandleEvent(EventType et, int errornum) + { + switch (et) + { + case EVENT_READ: + OnDataReady(); + break; + + case EVENT_WRITE: + OnWriteReady(); + break; + + case EVENT_ERROR: + DelayReconnect(); + break; + + default: + break; + } + } + + bool DoConnect() + { + if(!(sql = PQconnectStart(confhost.GetDSN().c_str()))) + return false; + + if(PQstatus(sql) == CONNECTION_BAD) + return false; + + if(PQsetnonblocking(sql, 1) == -1) + return false; + + /* OK, we've initalised the connection, now to get it hooked into the socket engine + * and then start polling it. + */ + this->fd = PQsocket(sql); + + if(this->fd <= -1) + return false; + + if (!this->Instance->SE->AddFd(this)) + { + Instance->Log(DEBUG, "BUG: Couldn't add pgsql socket to socket engine"); + return false; + } + + /* Socket all hooked into the engine, now to tell PgSQL to start connecting */ + return DoPoll(); + } + + bool DoPoll() + { + switch(PQconnectPoll(sql)) + { + case PGRES_POLLING_WRITING: + Instance->SE->WantWrite(this); + status = CWRITE; + return true; + case PGRES_POLLING_READING: + status = CREAD; + return true; + case PGRES_POLLING_FAILED: + return false; + case PGRES_POLLING_OK: + status = WWRITE; + return DoConnectedPoll(); + default: + return true; + } + } + + bool DoConnectedPoll() + { + if(!qinprog && queue.totalsize()) + { + /* There's no query currently in progress, and there's queries in the queue. */ + SQLrequest& query = queue.front(); + DoQuery(query); + } + + if(PQconsumeInput(sql)) + { + /* We just read stuff from the server, that counts as it being alive + * so update the idle-since time :p + */ + idle = this->Instance->Time(); + + if (PQisBusy(sql)) + { + /* Nothing happens here */ + } + else if (qinprog) + { + /* Grab the request we're processing */ + SQLrequest& query = queue.front(); + + /* Get a pointer to the module we're about to return the result to */ + Module* to = query.GetSource(); + + /* Fetch the result.. */ + PGresult* result = PQgetResult(sql); + + /* PgSQL would allow a query string to be sent which has multiple + * queries in it, this isn't portable across database backends and + * we don't want modules doing it. But just in case we make sure we + * drain any results there are and just use the last one. + * If the module devs are behaving there will only be one result. + */ + while (PGresult* temp = PQgetResult(sql)) + { + PQclear(result); + result = temp; + } + + if(to) + { + /* ..and the result */ + PgSQLresult reply(us, to, query.id, result); + + /* Fix by brain, make sure the original query gets sent back in the reply */ + reply.query = query.query.q; + + switch(PQresultStatus(result)) + { + case PGRES_EMPTY_QUERY: + case PGRES_BAD_RESPONSE: + case PGRES_FATAL_ERROR: + reply.error.Id(QREPLY_FAIL); + reply.error.Str(PQresultErrorMessage(result)); + default:; + /* No action, other values are not errors */ + } + + reply.Send(); + + /* PgSQLresult's destructor will free the PGresult */ + } + else + { + /* If the client module is unloaded partway through a query then the provider will set + * the pointer to NULL. We cannot just cancel the query as the result will still come + * through at some point...and it could get messy if we play with invalid pointers... + */ + PQclear(result); + } + qinprog = false; + queue.pop(); + DoConnectedPoll(); + } + return true; + } + else + { + /* I think we'll assume this means the server died...it might not, + * but I think that any error serious enough we actually get here + * deserves to reconnect [/excuse] + * Returning true so the core doesn't try and close the connection. + */ + DelayReconnect(); + return true; + } + } + + bool DoResetPoll() + { + switch(PQresetPoll(sql)) + { + case PGRES_POLLING_WRITING: + Instance->SE->WantWrite(this); + status = CWRITE; + return DoPoll(); + case PGRES_POLLING_READING: + status = CREAD; + return true; + case PGRES_POLLING_FAILED: + return false; + case PGRES_POLLING_OK: + status = WWRITE; + return DoConnectedPoll(); + default: + return true; + } + } + + bool OnDataReady() + { + /* Always return true here, false would close the socket - we need to do that ourselves with the pgsql API */ + return DoEvent(); + } + + bool OnWriteReady() + { + /* Always return true here, false would close the socket - we need to do that ourselves with the pgsql API */ + return DoEvent(); + } + + bool OnConnected() + { + return DoEvent(); + } + + void DelayReconnect(); + + bool DoEvent() + { + bool ret; + + if((status == CREAD) || (status == CWRITE)) + { + ret = DoPoll(); + } + else if((status == RREAD) || (status == RWRITE)) + { + ret = DoResetPoll(); + } + else + { + ret = DoConnectedPoll(); + } + return ret; + } + + SQLerror DoQuery(SQLrequest &req) + { + if((status == WREAD) || (status == WWRITE)) + { + if(!qinprog) + { + /* Parse the command string and dispatch it */ + + /* Pointer to the buffer we screw around with substitution in */ + char* query; + /* Pointer to the current end of query, where we append new stuff */ + char* queryend; + /* Total length of the unescaped parameters */ + unsigned int paramlen; + + paramlen = 0; + + for(ParamL::iterator i = req.query.p.begin(); i != req.query.p.end(); i++) + { + paramlen += i->size(); + } + + /* To avoid a lot of allocations, allocate enough memory for the biggest the escaped query could possibly be. + * sizeofquery + (totalparamlength*2) + 1 + * + * The +1 is for null-terminating the string for PQsendQuery() + */ + + query = new char[req.query.q.length() + (paramlen*2) + 1]; + queryend = query; + + /* Okay, now we have a buffer large enough we need to start copying the query into it and escaping and substituting + * the parameters into it... + */ + + for(unsigned int i = 0; i < req.query.q.length(); i++) + { + if(req.query.q[i] == '?') + { + /* We found a place to substitute..what fun. + * Use the PgSQL calls to escape and write the + * escaped string onto the end of our query buffer, + * then we "just" need to make sure queryend is + * pointing at the right place. + */ + + if(req.query.p.size()) + { + int error = 0; + size_t len = 0; + +#ifdef PGSQL_HAS_ESCAPECONN + len = PQescapeStringConn(sql, queryend, req.query.p.front().c_str(), req.query.p.front().length(), &error); +#else + len = PQescapeString (queryend, req.query.p.front().c_str(), req.query.p.front().length()); +#endif + if(error) + { + Instance->Log(DEBUG, "BUG: Apparently PQescapeStringConn() failed somehow...don't know how or what to do..."); + } + + /* Incremenet queryend to the end of the newly escaped parameter */ + queryend += len; + + /* Remove the parameter we just substituted in */ + req.query.p.pop_front(); + } + else + { + Instance->Log(DEBUG, "BUG: Found a substitution location but no parameter to substitute :|"); + break; + } + } + else + { + *queryend = req.query.q[i]; + queryend++; + } + } + + /* Null-terminate the query */ + *queryend = 0; + req.query.q = query; + + if(PQsendQuery(sql, query)) + { + qinprog = true; + delete[] query; + return SQLerror(); + } + else + { + delete[] query; + return SQLerror(QSEND_FAIL, PQerrorMessage(sql)); + } + } + } + return SQLerror(BAD_CONN, "Can't query until connection is complete"); + } + + SQLerror Query(const SQLrequest &req) + { + queue.push(req); + + if(!qinprog && queue.totalsize()) + { + /* There's no query currently in progress, and there's queries in the queue. */ + SQLrequest& query = queue.front(); + return DoQuery(query); + } + else + { + return SQLerror(); + } + } + + void OnUnloadModule(Module* mod) + { + queue.PurgeModule(mod); + } + + const SQLhost GetConfHost() + { + return confhost; + } + + void Close() { + if (!this->Instance->SE->DelFd(this)) + { + if (sql && PQstatus(sql) == CONNECTION_BAD) + { + this->Instance->SE->DelFd(this, true); + } + else + { + Instance->Log(DEBUG, "BUG: PQsocket cant be removed from socket engine!"); + } + } + + if(sql) + { + PQfinish(sql); + sql = NULL; + } + } + +}; + +class ModulePgSQL : public Module +{ + private: + ConnMap connections; + unsigned long currid; + char* sqlsuccess; + ReconnectTimer* retimer; + + public: + ModulePgSQL(InspIRCd* Me) + : Module::Module(Me), currid(0) + { + ServerInstance->UseInterface("SQLutils"); + + sqlsuccess = new char[strlen(SQLSUCCESS)+1]; + + strlcpy(sqlsuccess, SQLSUCCESS, strlen(SQLSUCCESS)); + + if (!ServerInstance->PublishFeature("SQL", this)) + { + throw ModuleException("BUG: PgSQL Unable to publish feature 'SQL'"); + } + + ReadConf(); + + ServerInstance->PublishInterface("SQL", this); + } + + virtual ~ModulePgSQL() + { + if (retimer) + ServerInstance->Timers->DelTimer(retimer); + ClearAllConnections(); + delete[] sqlsuccess; + ServerInstance->UnpublishInterface("SQL", this); + ServerInstance->UnpublishFeature("SQL"); + ServerInstance->DoneWithInterface("SQLutils"); + } + + void Implements(char* List) + { + List[I_OnUnloadModule] = List[I_OnRequest] = List[I_OnRehash] = List[I_OnUserRegister] = List[I_OnCheckReady] = List[I_OnUserDisconnect] = 1; + } + + virtual void OnRehash(userrec* user, const std::string ¶meter) + { + ReadConf(); + } + + bool HasHost(const SQLhost &host) + { + for (ConnMap::iterator iter = connections.begin(); iter != connections.end(); iter++) + { + if (host == iter->second->GetConfHost()) + return true; + } + return false; + } + + bool HostInConf(const SQLhost &h) + { + ConfigReader conf(ServerInstance); + for(int i = 0; i < conf.Enumerate("database"); i++) + { + SQLhost host; + host.id = conf.ReadValue("database", "id", i); + host.host = conf.ReadValue("database", "hostname", i); + host.port = conf.ReadInteger("database", "port", i, true); + host.name = conf.ReadValue("database", "name", i); + host.user = conf.ReadValue("database", "username", i); + host.pass = conf.ReadValue("database", "password", i); + host.ssl = conf.ReadFlag("database", "ssl", "0", i); + if (h == host) + return true; + } + return false; + } + + void ReadConf() + { + ClearOldConnections(); + + ConfigReader conf(ServerInstance); + for(int i = 0; i < conf.Enumerate("database"); i++) + { + SQLhost host; + int ipvalid; + + host.id = conf.ReadValue("database", "id", i); + host.host = conf.ReadValue("database", "hostname", i); + host.port = conf.ReadInteger("database", "port", i, true); + host.name = conf.ReadValue("database", "name", i); + host.user = conf.ReadValue("database", "username", i); + host.pass = conf.ReadValue("database", "password", i); + host.ssl = conf.ReadFlag("database", "ssl", "0", i); + + if (HasHost(host)) + continue; + +#ifdef IPV6 + if (strchr(host.host.c_str(),':')) + { + in6_addr blargle; + ipvalid = inet_pton(AF_INET6, host.host.c_str(), &blargle); + } + else +#endif + { + in_addr blargle; + ipvalid = inet_aton(host.host.c_str(), &blargle); + } + + if(ipvalid > 0) + { + /* The conversion succeeded, we were given an IP and we can give it straight to SQLConn */ + host.ip = host.host; + this->AddConn(host); + } + else if(ipvalid == 0) + { + /* Conversion failed, assume it's a host */ + SQLresolver* resolver; + + try + { + bool cached; + resolver = new SQLresolver(this, ServerInstance, host, cached); + ServerInstance->AddResolver(resolver, cached); + } + catch(...) + { + /* THE WORLD IS COMING TO AN END! */ + } + } + else + { + /* Invalid address family, die horribly. */ + ServerInstance->Log(DEBUG, "BUG: insp_aton failed returning -1, oh noes."); + } + } + } + + void ClearOldConnections() + { + ConnMap::iterator iter,safei; + for (iter = connections.begin(); iter != connections.end(); iter++) + { + if (!HostInConf(iter->second->GetConfHost())) + { + DELETE(iter->second); + safei = iter; + --iter; + connections.erase(safei); + } + } + } + + void ClearAllConnections() + { + ConnMap::iterator i; + while ((i = connections.begin()) != connections.end()) + { + connections.erase(i); + DELETE(i->second); + } + } + + void AddConn(const SQLhost& hi) + { + if (HasHost(hi)) + { + ServerInstance->Log(DEFAULT, "WARNING: A pgsql connection with id: %s already exists, possibly due to DNS delay. Aborting connection attempt.", hi.id.c_str()); + return; + } + + SQLConn* newconn; + + /* The conversion succeeded, we were given an IP and we can give it straight to SQLConn */ + newconn = new SQLConn(ServerInstance, this, hi); + + connections.insert(std::make_pair(hi.id, newconn)); + } + + void ReconnectConn(SQLConn* conn) + { + for (ConnMap::iterator iter = connections.begin(); iter != connections.end(); iter++) + { + if (conn == iter->second) + { + DELETE(iter->second); + connections.erase(iter); + break; + } + } + retimer = new ReconnectTimer(ServerInstance, this); + ServerInstance->Timers->AddTimer(retimer); + } + + virtual char* OnRequest(Request* request) + { + if(strcmp(SQLREQID, request->GetId()) == 0) + { + SQLrequest* req = (SQLrequest*)request; + ConnMap::iterator iter; + if((iter = connections.find(req->dbid)) != connections.end()) + { + /* Execute query */ + req->id = NewID(); + req->error = iter->second->Query(*req); + + return (req->error.Id() == NO_ERROR) ? sqlsuccess : NULL; + } + else + { + req->error.Id(BAD_DBID); + return NULL; + } + } + return NULL; + } + + virtual void OnUnloadModule(Module* mod, const std::string& name) + { + /* When a module unloads we have to check all the pending queries for all our connections + * and set the Module* specifying where the query came from to NULL. If the query has already + * been dispatched then when it is processed it will be dropped if the pointer is NULL. + * + * If the queries we find are not already being executed then we can simply remove them immediately. + */ + for(ConnMap::iterator iter = connections.begin(); iter != connections.end(); iter++) + { + iter->second->OnUnloadModule(mod); + } + } + + unsigned long NewID() + { + if (currid+1 == 0) + currid++; + + return ++currid; + } + + virtual Version GetVersion() + { + return Version(1, 1, 0, 0, VF_VENDOR|VF_SERVICEPROVIDER, API_VERSION); + } +}; + +/* move this here to use AddConn, rather that than having the whole + * module above SQLConn, since this is buggin me right now :/ + */ +void SQLresolver::OnLookupComplete(const std::string &result, unsigned int ttl, bool cached) +{ + host.ip = result; + ((ModulePgSQL*)mod)->AddConn(host); + ((ModulePgSQL*)mod)->ClearOldConnections(); +} + +void ReconnectTimer::Tick(time_t time) +{ + ((ModulePgSQL*)mod)->ReadConf(); +} + +void SQLConn::DelayReconnect() +{ + ((ModulePgSQL*)us)->ReconnectConn(this); +} + +MODULE_INIT(ModulePgSQL); + diff --git a/src/modules/extra/m_sqlauth.cpp b/src/modules/extra/m_sqlauth.cpp index 862929919..6b05ee521 100644 --- a/src/modules/extra/m_sqlauth.cpp +++ b/src/modules/extra/m_sqlauth.cpp @@ -1 +1,194 @@ -/* +------------------------------------+
* | 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 "users.h"
#include "channels.h"
#include "modules.h"
#include "m_sqlv2.h"
#include "m_sqlutils.h"
/* $ModDesc: Allow/Deny connections based upon an arbitary SQL table */
/* $ModDep: m_sqlv2.h m_sqlutils.h */
class ModuleSQLAuth : public Module
{
Module* SQLutils;
Module* SQLprovider;
std::string usertable;
std::string userfield;
std::string passfield;
std::string encryption;
std::string killreason;
std::string allowpattern;
std::string databaseid;
bool verbose;
public:
ModuleSQLAuth(InspIRCd* Me)
: Module::Module(Me)
{
ServerInstance->UseInterface("SQLutils");
ServerInstance->UseInterface("SQL");
SQLutils = ServerInstance->FindModule("m_sqlutils.so");
if (!SQLutils)
throw ModuleException("Can't find m_sqlutils.so. Please load m_sqlutils.so before m_sqlauth.so.");
SQLprovider = ServerInstance->FindFeature("SQL");
if (!SQLprovider)
throw ModuleException("Can't find an SQL provider module. Please load one before attempting to load m_sqlauth.");
OnRehash(NULL,"");
}
virtual ~ModuleSQLAuth()
{
ServerInstance->DoneWithInterface("SQL");
ServerInstance->DoneWithInterface("SQLutils");
}
void Implements(char* List)
{
List[I_OnUserDisconnect] = List[I_OnCheckReady] = List[I_OnRequest] = List[I_OnRehash] = List[I_OnUserRegister] = 1;
}
virtual void OnRehash(userrec* user, const std::string ¶meter)
{
ConfigReader Conf(ServerInstance);
usertable = Conf.ReadValue("sqlauth", "usertable", 0); /* User table name */
databaseid = Conf.ReadValue("sqlauth", "dbid", 0); /* Database ID, given to the SQL service provider */
userfield = Conf.ReadValue("sqlauth", "userfield", 0); /* Field name where username can be found */
passfield = Conf.ReadValue("sqlauth", "passfield", 0); /* Field name where password can be found */
killreason = Conf.ReadValue("sqlauth", "killreason", 0); /* Reason to give when access is denied to a user (put your reg details here) */
allowpattern= Conf.ReadValue("sqlauth", "allowpattern",0 ); /* Allow nicks matching this pattern without requiring auth */
encryption = Conf.ReadValue("sqlauth", "encryption", 0); /* Name of sql function used to encrypt password, e.g. "md5" or "passwd".
* define, but leave blank if no encryption is to be used.
*/
verbose = Conf.ReadFlag("sqlauth", "verbose", 0); /* Set to true if failed connects should be reported to operators */
if (encryption.find("(") == std::string::npos)
{
encryption.append("(");
}
}
virtual int OnUserRegister(userrec* user)
{
if ((!allowpattern.empty()) && (ServerInstance->MatchText(user->nick,allowpattern)))
{
user->Extend("sqlauthed");
return 0;
}
if (!CheckCredentials(user))
{
userrec::QuitUser(ServerInstance,user,killreason);
return 1;
}
return 0;
}
bool CheckCredentials(userrec* user)
{
SQLrequest req = SQLreq(this, SQLprovider, databaseid, "SELECT ? FROM ? WHERE ? = '?' AND ? = ?'?')", userfield, usertable, userfield, user->nick, passfield, encryption, user->password);
if(req.Send())
{
/* When we get the query response from the service provider we will be given an ID to play with,
* just an ID number which is unique to this query. We need a way of associating that ID with a userrec
* so we insert it into a map mapping the IDs to users.
* Thankfully m_sqlutils provides this, it will associate a ID with a user or channel, and if the user quits it removes the
* association. This means that if the user quits during a query we will just get a failed lookup from m_sqlutils - telling
* us to discard the query.
*/
AssociateUser(this, SQLutils, req.id, user).Send();
return true;
}
else
{
if (verbose)
ServerInstance->WriteOpers("Forbidden connection from %s!%s@%s (SQL query failed: %s)", user->nick, user->ident, user->host, req.error.Str());
return false;
}
}
virtual char* OnRequest(Request* request)
{
if(strcmp(SQLRESID, request->GetId()) == 0)
{
SQLresult* res = static_cast<SQLresult*>(request);
userrec* user = GetAssocUser(this, SQLutils, res->id).S().user;
UnAssociate(this, SQLutils, res->id).S();
if(user)
{
if(res->error.Id() == NO_ERROR)
{
if(res->Rows())
{
/* We got a row in the result, this is enough really */
user->Extend("sqlauthed");
}
else if (verbose)
{
/* No rows in result, this means there was no record matching the user */
ServerInstance->WriteOpers("Forbidden connection from %s!%s@%s (SQL query returned no matches)", user->nick, user->ident, user->host);
user->Extend("sqlauth_failed");
}
}
else if (verbose)
{
ServerInstance->WriteOpers("Forbidden connection from %s!%s@%s (SQL query failed: %s)", user->nick, user->ident, user->host, res->error.Str());
user->Extend("sqlauth_failed");
}
}
else
{
return NULL;
}
if (!user->GetExt("sqlauthed"))
{
userrec::QuitUser(ServerInstance,user,killreason);
}
return SQLSUCCESS;
}
return NULL;
}
virtual void OnUserDisconnect(userrec* user)
{
user->Shrink("sqlauthed");
user->Shrink("sqlauth_failed");
}
virtual bool OnCheckReady(userrec* user)
{
return user->GetExt("sqlauthed");
}
virtual Version GetVersion()
{
return Version(1,1,1,0,VF_VENDOR,API_VERSION);
}
};
MODULE_INIT(ModuleSQLAuth);
\ No newline at end of file +/* +------------------------------------+ + * | 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 "users.h" +#include "channels.h" +#include "modules.h" +#include "m_sqlv2.h" +#include "m_sqlutils.h" + +/* $ModDesc: Allow/Deny connections based upon an arbitary SQL table */ +/* $ModDep: m_sqlv2.h m_sqlutils.h */ + +class ModuleSQLAuth : public Module +{ + Module* SQLutils; + Module* SQLprovider; + + std::string usertable; + std::string userfield; + std::string passfield; + std::string encryption; + std::string killreason; + std::string allowpattern; + std::string databaseid; + + bool verbose; + +public: + ModuleSQLAuth(InspIRCd* Me) + : Module::Module(Me) + { + ServerInstance->UseInterface("SQLutils"); + ServerInstance->UseInterface("SQL"); + + SQLutils = ServerInstance->FindModule("m_sqlutils.so"); + if (!SQLutils) + throw ModuleException("Can't find m_sqlutils.so. Please load m_sqlutils.so before m_sqlauth.so."); + + SQLprovider = ServerInstance->FindFeature("SQL"); + if (!SQLprovider) + throw ModuleException("Can't find an SQL provider module. Please load one before attempting to load m_sqlauth."); + + OnRehash(NULL,""); + } + + virtual ~ModuleSQLAuth() + { + ServerInstance->DoneWithInterface("SQL"); + ServerInstance->DoneWithInterface("SQLutils"); + } + + void Implements(char* List) + { + List[I_OnUserDisconnect] = List[I_OnCheckReady] = List[I_OnRequest] = List[I_OnRehash] = List[I_OnUserRegister] = 1; + } + + virtual void OnRehash(userrec* user, const std::string ¶meter) + { + ConfigReader Conf(ServerInstance); + + usertable = Conf.ReadValue("sqlauth", "usertable", 0); /* User table name */ + databaseid = Conf.ReadValue("sqlauth", "dbid", 0); /* Database ID, given to the SQL service provider */ + userfield = Conf.ReadValue("sqlauth", "userfield", 0); /* Field name where username can be found */ + passfield = Conf.ReadValue("sqlauth", "passfield", 0); /* Field name where password can be found */ + killreason = Conf.ReadValue("sqlauth", "killreason", 0); /* Reason to give when access is denied to a user (put your reg details here) */ + allowpattern= Conf.ReadValue("sqlauth", "allowpattern",0 ); /* Allow nicks matching this pattern without requiring auth */ + encryption = Conf.ReadValue("sqlauth", "encryption", 0); /* Name of sql function used to encrypt password, e.g. "md5" or "passwd". + * define, but leave blank if no encryption is to be used. + */ + verbose = Conf.ReadFlag("sqlauth", "verbose", 0); /* Set to true if failed connects should be reported to operators */ + + if (encryption.find("(") == std::string::npos) + { + encryption.append("("); + } + } + + virtual int OnUserRegister(userrec* user) + { + if ((!allowpattern.empty()) && (ServerInstance->MatchText(user->nick,allowpattern))) + { + user->Extend("sqlauthed"); + return 0; + } + + if (!CheckCredentials(user)) + { + userrec::QuitUser(ServerInstance,user,killreason); + return 1; + } + return 0; + } + + bool CheckCredentials(userrec* user) + { + SQLrequest req = SQLreq(this, SQLprovider, databaseid, "SELECT ? FROM ? WHERE ? = '?' AND ? = ?'?')", userfield, usertable, userfield, user->nick, passfield, encryption, user->password); + + if(req.Send()) + { + /* When we get the query response from the service provider we will be given an ID to play with, + * just an ID number which is unique to this query. We need a way of associating that ID with a userrec + * so we insert it into a map mapping the IDs to users. + * Thankfully m_sqlutils provides this, it will associate a ID with a user or channel, and if the user quits it removes the + * association. This means that if the user quits during a query we will just get a failed lookup from m_sqlutils - telling + * us to discard the query. + */ + AssociateUser(this, SQLutils, req.id, user).Send(); + + return true; + } + else + { + if (verbose) + ServerInstance->WriteOpers("Forbidden connection from %s!%s@%s (SQL query failed: %s)", user->nick, user->ident, user->host, req.error.Str()); + return false; + } + } + + virtual char* OnRequest(Request* request) + { + if(strcmp(SQLRESID, request->GetId()) == 0) + { + SQLresult* res = static_cast<SQLresult*>(request); + + userrec* user = GetAssocUser(this, SQLutils, res->id).S().user; + UnAssociate(this, SQLutils, res->id).S(); + + if(user) + { + if(res->error.Id() == NO_ERROR) + { + if(res->Rows()) + { + /* We got a row in the result, this is enough really */ + user->Extend("sqlauthed"); + } + else if (verbose) + { + /* No rows in result, this means there was no record matching the user */ + ServerInstance->WriteOpers("Forbidden connection from %s!%s@%s (SQL query returned no matches)", user->nick, user->ident, user->host); + user->Extend("sqlauth_failed"); + } + } + else if (verbose) + { + ServerInstance->WriteOpers("Forbidden connection from %s!%s@%s (SQL query failed: %s)", user->nick, user->ident, user->host, res->error.Str()); + user->Extend("sqlauth_failed"); + } + } + else + { + return NULL; + } + + if (!user->GetExt("sqlauthed")) + { + userrec::QuitUser(ServerInstance,user,killreason); + } + return SQLSUCCESS; + } + return NULL; + } + + virtual void OnUserDisconnect(userrec* user) + { + user->Shrink("sqlauthed"); + user->Shrink("sqlauth_failed"); + } + + virtual bool OnCheckReady(userrec* user) + { + return user->GetExt("sqlauthed"); + } + + virtual Version GetVersion() + { + return Version(1,1,1,0,VF_VENDOR,API_VERSION); + } + +}; + +MODULE_INIT(ModuleSQLAuth); + diff --git a/src/modules/extra/m_sqlite3.cpp b/src/modules/extra/m_sqlite3.cpp index 6741d7745..66955de07 100644 --- a/src/modules/extra/m_sqlite3.cpp +++ b/src/modules/extra/m_sqlite3.cpp @@ -1 +1,660 @@ -/* +------------------------------------+
* | 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 <sqlite3.h>
#include "users.h"
#include "channels.h"
#include "modules.h"
#include "m_sqlv2.h"
/* $ModDesc: sqlite3 provider */
/* $CompileFlags: pkgconfversion("sqlite3","3.3") pkgconfincludes("sqlite3","/sqlite3.h","") */
/* $LinkerFlags: pkgconflibs("sqlite3","/libsqlite3.so","-lsqlite3") */
/* $ModDep: m_sqlv2.h */
class SQLConn;
class SQLite3Result;
class ResultNotifier;
typedef std::map<std::string, SQLConn*> ConnMap;
typedef std::deque<classbase*> paramlist;
typedef std::deque<SQLite3Result*> ResultQueue;
ResultNotifier* resultnotify = NULL;
class ResultNotifier : public InspSocket
{
Module* mod;
insp_sockaddr sock_us;
socklen_t uslen;
public:
/* Create a socket on a random port. Let the tcp stack allocate us an available port */
#ifdef IPV6
ResultNotifier(InspIRCd* SI, Module* m) : InspSocket(SI, "::1", 0, true, 3000), mod(m)
#else
ResultNotifier(InspIRCd* SI, Module* m) : InspSocket(SI, "127.0.0.1", 0, true, 3000), mod(m)
#endif
{
uslen = sizeof(sock_us);
if (getsockname(this->fd,(sockaddr*)&sock_us,&uslen))
{
throw ModuleException("Could not create random listening port on localhost");
}
}
ResultNotifier(InspIRCd* SI, Module* m, int newfd, char* ip) : InspSocket(SI, newfd, ip), mod(m)
{
}
/* Using getsockname and ntohs, we can determine which port number we were allocated */
int GetPort()
{
#ifdef IPV6
return ntohs(sock_us.sin6_port);
#else
return ntohs(sock_us.sin_port);
#endif
}
virtual int OnIncomingConnection(int newsock, char* ip)
{
Dispatch();
return false;
}
void Dispatch();
};
class SQLite3Result : public SQLresult
{
private:
int currentrow;
int rows;
int cols;
std::vector<std::string> colnames;
std::vector<SQLfieldList> fieldlists;
SQLfieldList emptyfieldlist;
SQLfieldList* fieldlist;
SQLfieldMap* fieldmap;
public:
SQLite3Result(Module* self, Module* to, unsigned int id)
: SQLresult(self, to, id), currentrow(0), rows(0), cols(0), fieldlist(NULL), fieldmap(NULL)
{
}
~SQLite3Result()
{
}
void AddRow(int colsnum, char **data, char **colname)
{
colnames.clear();
cols = colsnum;
for (int i = 0; i < colsnum; i++)
{
fieldlists.resize(fieldlists.size()+1);
colnames.push_back(colname[i]);
SQLfield sf(data[i] ? data[i] : "", data[i] ? false : true);
fieldlists[rows].push_back(sf);
}
rows++;
}
void UpdateAffectedCount()
{
rows++;
}
virtual int Rows()
{
return rows;
}
virtual int Cols()
{
return cols;
}
virtual std::string ColName(int column)
{
if (column < (int)colnames.size())
{
return colnames[column];
}
else
{
throw SQLbadColName();
}
return "";
}
virtual int ColNum(const std::string &column)
{
for (unsigned int i = 0; i < colnames.size(); i++)
{
if (column == colnames[i])
return i;
}
throw SQLbadColName();
return 0;
}
virtual SQLfield GetValue(int row, int column)
{
if ((row >= 0) && (row < rows) && (column >= 0) && (column < Cols()))
{
return fieldlists[row][column];
}
throw SQLbadColName();
/* XXX: We never actually get here because of the throw */
return SQLfield("",true);
}
virtual SQLfieldList& GetRow()
{
if (currentrow < rows)
return fieldlists[currentrow];
else
return emptyfieldlist;
}
virtual SQLfieldMap& GetRowMap()
{
/* In an effort to reduce overhead we don't actually allocate the map
* until the first time it's needed...so...
*/
if(fieldmap)
{
fieldmap->clear();
}
else
{
fieldmap = new SQLfieldMap;
}
if (currentrow < rows)
{
for (int i = 0; i < Cols(); i++)
{
fieldmap->insert(std::make_pair(ColName(i), GetValue(currentrow, i)));
}
currentrow++;
}
return *fieldmap;
}
virtual SQLfieldList* GetRowPtr()
{
fieldlist = new SQLfieldList();
if (currentrow < rows)
{
for (int i = 0; i < Rows(); i++)
{
fieldlist->push_back(fieldlists[currentrow][i]);
}
currentrow++;
}
return fieldlist;
}
virtual SQLfieldMap* GetRowMapPtr()
{
fieldmap = new SQLfieldMap();
if (currentrow < rows)
{
for (int i = 0; i < Cols(); i++)
{
fieldmap->insert(std::make_pair(colnames[i],GetValue(currentrow, i)));
}
currentrow++;
}
return fieldmap;
}
virtual void Free(SQLfieldMap* fm)
{
delete fm;
}
virtual void Free(SQLfieldList* fl)
{
delete fl;
}
};
class SQLConn : public classbase
{
private:
ResultQueue results;
InspIRCd* Instance;
Module* mod;
SQLhost host;
sqlite3* conn;
public:
SQLConn(InspIRCd* SI, Module* m, const SQLhost& hi)
: Instance(SI), mod(m), host(hi)
{
if (OpenDB() != SQLITE_OK)
{
Instance->Log(DEFAULT, "WARNING: Could not open DB with id: " + host.id);
CloseDB();
}
}
~SQLConn()
{
CloseDB();
}
SQLerror Query(SQLrequest &req)
{
/* Pointer to the buffer we screw around with substitution in */
char* query;
/* Pointer to the current end of query, where we append new stuff */
char* queryend;
/* Total length of the unescaped parameters */
unsigned long paramlen;
/* Total length of query, used for binary-safety in mysql_real_query */
unsigned long querylength = 0;
paramlen = 0;
for(ParamL::iterator i = req.query.p.begin(); i != req.query.p.end(); i++)
{
paramlen += i->size();
}
/* To avoid a lot of allocations, allocate enough memory for the biggest the escaped query could possibly be.
* sizeofquery + (totalparamlength*2) + 1
*
* The +1 is for null-terminating the string for mysql_real_escape_string
*/
query = new char[req.query.q.length() + (paramlen*2) + 1];
queryend = query;
for(unsigned long i = 0; i < req.query.q.length(); i++)
{
if(req.query.q[i] == '?')
{
if(req.query.p.size())
{
char* escaped;
escaped = sqlite3_mprintf("%q", req.query.p.front().c_str());
for (char* n = escaped; *n; n++)
{
*queryend = *n;
queryend++;
}
sqlite3_free(escaped);
req.query.p.pop_front();
}
else
break;
}
else
{
*queryend = req.query.q[i];
queryend++;
}
querylength++;
}
*queryend = 0;
req.query.q = query;
SQLite3Result* res = new SQLite3Result(mod, req.GetSource(), req.id);
res->dbid = host.id;
res->query = req.query.q;
paramlist params;
params.push_back(this);
params.push_back(res);
char *errmsg = 0;
sqlite3_update_hook(conn, QueryUpdateHook, ¶ms);
if (sqlite3_exec(conn, req.query.q.data(), QueryResult, ¶ms, &errmsg) != SQLITE_OK)
{
std::string error(errmsg);
sqlite3_free(errmsg);
delete[] query;
delete res;
return SQLerror(QSEND_FAIL, error);
}
delete[] query;
results.push_back(res);
SendNotify();
return SQLerror();
}
static int QueryResult(void *params, int argc, char **argv, char **azColName)
{
paramlist* p = (paramlist*)params;
((SQLConn*)(*p)[0])->ResultReady(((SQLite3Result*)(*p)[1]), argc, argv, azColName);
return 0;
}
static void QueryUpdateHook(void *params, int eventid, char const * azSQLite, char const * azColName, sqlite_int64 rowid)
{
paramlist* p = (paramlist*)params;
((SQLConn*)(*p)[0])->AffectedReady(((SQLite3Result*)(*p)[1]));
}
void ResultReady(SQLite3Result *res, int cols, char **data, char **colnames)
{
res->AddRow(cols, data, colnames);
}
void AffectedReady(SQLite3Result *res)
{
res->UpdateAffectedCount();
}
int OpenDB()
{
return sqlite3_open(host.host.c_str(), &conn);
}
void CloseDB()
{
sqlite3_interrupt(conn);
sqlite3_close(conn);
}
SQLhost GetConfHost()
{
return host;
}
void SendResults()
{
while (results.size())
{
SQLite3Result* res = results[0];
if (res->GetDest())
{
res->Send();
}
else
{
/* If the client module is unloaded partway through a query then the provider will set
* the pointer to NULL. We cannot just cancel the query as the result will still come
* through at some point...and it could get messy if we play with invalid pointers...
*/
delete res;
}
results.pop_front();
}
}
void ClearResults()
{
while (results.size())
{
SQLite3Result* res = results[0];
delete res;
results.pop_front();
}
}
void SendNotify()
{
int QueueFD;
if ((QueueFD = socket(AF_FAMILY, SOCK_STREAM, 0)) == -1)
{
/* crap, we're out of sockets... */
return;
}
insp_sockaddr addr;
#ifdef IPV6
insp_aton("::1", &addr.sin6_addr);
addr.sin6_family = AF_FAMILY;
addr.sin6_port = htons(resultnotify->GetPort());
#else
insp_inaddr ia;
insp_aton("127.0.0.1", &ia);
addr.sin_family = AF_FAMILY;
addr.sin_addr = ia;
addr.sin_port = htons(resultnotify->GetPort());
#endif
if (connect(QueueFD, (sockaddr*)&addr,sizeof(addr)) == -1)
{
/* wtf, we cant connect to it, but we just created it! */
return;
}
}
};
class ModuleSQLite3 : public Module
{
private:
ConnMap connections;
unsigned long currid;
public:
ModuleSQLite3(InspIRCd* Me)
: Module::Module(Me), currid(0)
{
ServerInstance->UseInterface("SQLutils");
if (!ServerInstance->PublishFeature("SQL", this))
{
throw ModuleException("m_sqlite3: Unable to publish feature 'SQL'");
}
resultnotify = new ResultNotifier(ServerInstance, this);
ReadConf();
ServerInstance->PublishInterface("SQL", this);
}
virtual ~ModuleSQLite3()
{
ClearQueue();
ClearAllConnections();
resultnotify->SetFd(-1);
resultnotify->state = I_ERROR;
resultnotify->OnError(I_ERR_SOCKET);
resultnotify->ClosePending = true;
delete resultnotify;
ServerInstance->UnpublishInterface("SQL", this);
ServerInstance->UnpublishFeature("SQL");
ServerInstance->DoneWithInterface("SQLutils");
}
void Implements(char* List)
{
List[I_OnRequest] = List[I_OnRehash] = 1;
}
void SendQueue()
{
for (ConnMap::iterator iter = connections.begin(); iter != connections.end(); iter++)
{
iter->second->SendResults();
}
}
void ClearQueue()
{
for (ConnMap::iterator iter = connections.begin(); iter != connections.end(); iter++)
{
iter->second->ClearResults();
}
}
bool HasHost(const SQLhost &host)
{
for (ConnMap::iterator iter = connections.begin(); iter != connections.end(); iter++)
{
if (host == iter->second->GetConfHost())
return true;
}
return false;
}
bool HostInConf(const SQLhost &h)
{
ConfigReader conf(ServerInstance);
for(int i = 0; i < conf.Enumerate("database"); i++)
{
SQLhost host;
host.id = conf.ReadValue("database", "id", i);
host.host = conf.ReadValue("database", "hostname", i);
host.port = conf.ReadInteger("database", "port", i, true);
host.name = conf.ReadValue("database", "name", i);
host.user = conf.ReadValue("database", "username", i);
host.pass = conf.ReadValue("database", "password", i);
host.ssl = conf.ReadFlag("database", "ssl", "0", i);
if (h == host)
return true;
}
return false;
}
void ReadConf()
{
ClearOldConnections();
ConfigReader conf(ServerInstance);
for(int i = 0; i < conf.Enumerate("database"); i++)
{
SQLhost host;
host.id = conf.ReadValue("database", "id", i);
host.host = conf.ReadValue("database", "hostname", i);
host.port = conf.ReadInteger("database", "port", i, true);
host.name = conf.ReadValue("database", "name", i);
host.user = conf.ReadValue("database", "username", i);
host.pass = conf.ReadValue("database", "password", i);
host.ssl = conf.ReadFlag("database", "ssl", "0", i);
if (HasHost(host))
continue;
this->AddConn(host);
}
}
void AddConn(const SQLhost& hi)
{
if (HasHost(hi))
{
ServerInstance->Log(DEFAULT, "WARNING: A sqlite connection with id: %s already exists. Aborting database open attempt.", hi.id.c_str());
return;
}
SQLConn* newconn;
newconn = new SQLConn(ServerInstance, this, hi);
connections.insert(std::make_pair(hi.id, newconn));
}
void ClearOldConnections()
{
ConnMap::iterator iter,safei;
for (iter = connections.begin(); iter != connections.end(); iter++)
{
if (!HostInConf(iter->second->GetConfHost()))
{
DELETE(iter->second);
safei = iter;
--iter;
connections.erase(safei);
}
}
}
void ClearAllConnections()
{
ConnMap::iterator i;
while ((i = connections.begin()) != connections.end())
{
connections.erase(i);
DELETE(i->second);
}
}
virtual void OnRehash(userrec* user, const std::string ¶meter)
{
ReadConf();
}
virtual char* OnRequest(Request* request)
{
if(strcmp(SQLREQID, request->GetId()) == 0)
{
SQLrequest* req = (SQLrequest*)request;
ConnMap::iterator iter;
if((iter = connections.find(req->dbid)) != connections.end())
{
req->id = NewID();
req->error = iter->second->Query(*req);
return SQLSUCCESS;
}
else
{
req->error.Id(BAD_DBID);
return NULL;
}
}
return NULL;
}
unsigned long NewID()
{
if (currid+1 == 0)
currid++;
return ++currid;
}
virtual Version GetVersion()
{
return Version(1,1,0,0,VF_VENDOR|VF_SERVICEPROVIDER,API_VERSION);
}
};
void ResultNotifier::Dispatch()
{
((ModuleSQLite3*)mod)->SendQueue();
}
MODULE_INIT(ModuleSQLite3);
\ No newline at end of file +/* +------------------------------------+ + * | 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 <sqlite3.h> +#include "users.h" +#include "channels.h" +#include "modules.h" + +#include "m_sqlv2.h" + +/* $ModDesc: sqlite3 provider */ +/* $CompileFlags: pkgconfversion("sqlite3","3.3") pkgconfincludes("sqlite3","/sqlite3.h","") */ +/* $LinkerFlags: pkgconflibs("sqlite3","/libsqlite3.so","-lsqlite3") */ +/* $ModDep: m_sqlv2.h */ + + +class SQLConn; +class SQLite3Result; +class ResultNotifier; + +typedef std::map<std::string, SQLConn*> ConnMap; +typedef std::deque<classbase*> paramlist; +typedef std::deque<SQLite3Result*> ResultQueue; + +ResultNotifier* resultnotify = NULL; + + +class ResultNotifier : public InspSocket +{ + Module* mod; + insp_sockaddr sock_us; + socklen_t uslen; + + public: + /* Create a socket on a random port. Let the tcp stack allocate us an available port */ +#ifdef IPV6 + ResultNotifier(InspIRCd* SI, Module* m) : InspSocket(SI, "::1", 0, true, 3000), mod(m) +#else + ResultNotifier(InspIRCd* SI, Module* m) : InspSocket(SI, "127.0.0.1", 0, true, 3000), mod(m) +#endif + { + uslen = sizeof(sock_us); + if (getsockname(this->fd,(sockaddr*)&sock_us,&uslen)) + { + throw ModuleException("Could not create random listening port on localhost"); + } + } + + ResultNotifier(InspIRCd* SI, Module* m, int newfd, char* ip) : InspSocket(SI, newfd, ip), mod(m) + { + } + + /* Using getsockname and ntohs, we can determine which port number we were allocated */ + int GetPort() + { +#ifdef IPV6 + return ntohs(sock_us.sin6_port); +#else + return ntohs(sock_us.sin_port); +#endif + } + + virtual int OnIncomingConnection(int newsock, char* ip) + { + Dispatch(); + return false; + } + + void Dispatch(); +}; + + +class SQLite3Result : public SQLresult +{ + private: + int currentrow; + int rows; + int cols; + + std::vector<std::string> colnames; + std::vector<SQLfieldList> fieldlists; + SQLfieldList emptyfieldlist; + + SQLfieldList* fieldlist; + SQLfieldMap* fieldmap; + + public: + SQLite3Result(Module* self, Module* to, unsigned int id) + : SQLresult(self, to, id), currentrow(0), rows(0), cols(0), fieldlist(NULL), fieldmap(NULL) + { + } + + ~SQLite3Result() + { + } + + void AddRow(int colsnum, char **data, char **colname) + { + colnames.clear(); + cols = colsnum; + for (int i = 0; i < colsnum; i++) + { + fieldlists.resize(fieldlists.size()+1); + colnames.push_back(colname[i]); + SQLfield sf(data[i] ? data[i] : "", data[i] ? false : true); + fieldlists[rows].push_back(sf); + } + rows++; + } + + void UpdateAffectedCount() + { + rows++; + } + + virtual int Rows() + { + return rows; + } + + virtual int Cols() + { + return cols; + } + + virtual std::string ColName(int column) + { + if (column < (int)colnames.size()) + { + return colnames[column]; + } + else + { + throw SQLbadColName(); + } + return ""; + } + + virtual int ColNum(const std::string &column) + { + for (unsigned int i = 0; i < colnames.size(); i++) + { + if (column == colnames[i]) + return i; + } + throw SQLbadColName(); + return 0; + } + + virtual SQLfield GetValue(int row, int column) + { + if ((row >= 0) && (row < rows) && (column >= 0) && (column < Cols())) + { + return fieldlists[row][column]; + } + + throw SQLbadColName(); + + /* XXX: We never actually get here because of the throw */ + return SQLfield("",true); + } + + virtual SQLfieldList& GetRow() + { + if (currentrow < rows) + return fieldlists[currentrow]; + else + return emptyfieldlist; + } + + virtual SQLfieldMap& GetRowMap() + { + /* In an effort to reduce overhead we don't actually allocate the map + * until the first time it's needed...so... + */ + if(fieldmap) + { + fieldmap->clear(); + } + else + { + fieldmap = new SQLfieldMap; + } + + if (currentrow < rows) + { + for (int i = 0; i < Cols(); i++) + { + fieldmap->insert(std::make_pair(ColName(i), GetValue(currentrow, i))); + } + currentrow++; + } + + return *fieldmap; + } + + virtual SQLfieldList* GetRowPtr() + { + fieldlist = new SQLfieldList(); + + if (currentrow < rows) + { + for (int i = 0; i < Rows(); i++) + { + fieldlist->push_back(fieldlists[currentrow][i]); + } + currentrow++; + } + return fieldlist; + } + + virtual SQLfieldMap* GetRowMapPtr() + { + fieldmap = new SQLfieldMap(); + + if (currentrow < rows) + { + for (int i = 0; i < Cols(); i++) + { + fieldmap->insert(std::make_pair(colnames[i],GetValue(currentrow, i))); + } + currentrow++; + } + + return fieldmap; + } + + virtual void Free(SQLfieldMap* fm) + { + delete fm; + } + + virtual void Free(SQLfieldList* fl) + { + delete fl; + } + + +}; + +class SQLConn : public classbase +{ + private: + ResultQueue results; + InspIRCd* Instance; + Module* mod; + SQLhost host; + sqlite3* conn; + + public: + SQLConn(InspIRCd* SI, Module* m, const SQLhost& hi) + : Instance(SI), mod(m), host(hi) + { + if (OpenDB() != SQLITE_OK) + { + Instance->Log(DEFAULT, "WARNING: Could not open DB with id: " + host.id); + CloseDB(); + } + } + + ~SQLConn() + { + CloseDB(); + } + + SQLerror Query(SQLrequest &req) + { + /* Pointer to the buffer we screw around with substitution in */ + char* query; + + /* Pointer to the current end of query, where we append new stuff */ + char* queryend; + + /* Total length of the unescaped parameters */ + unsigned long paramlen; + + /* Total length of query, used for binary-safety in mysql_real_query */ + unsigned long querylength = 0; + + paramlen = 0; + for(ParamL::iterator i = req.query.p.begin(); i != req.query.p.end(); i++) + { + paramlen += i->size(); + } + + /* To avoid a lot of allocations, allocate enough memory for the biggest the escaped query could possibly be. + * sizeofquery + (totalparamlength*2) + 1 + * + * The +1 is for null-terminating the string for mysql_real_escape_string + */ + query = new char[req.query.q.length() + (paramlen*2) + 1]; + queryend = query; + + for(unsigned long i = 0; i < req.query.q.length(); i++) + { + if(req.query.q[i] == '?') + { + if(req.query.p.size()) + { + char* escaped; + escaped = sqlite3_mprintf("%q", req.query.p.front().c_str()); + for (char* n = escaped; *n; n++) + { + *queryend = *n; + queryend++; + } + sqlite3_free(escaped); + req.query.p.pop_front(); + } + else + break; + } + else + { + *queryend = req.query.q[i]; + queryend++; + } + querylength++; + } + *queryend = 0; + req.query.q = query; + + SQLite3Result* res = new SQLite3Result(mod, req.GetSource(), req.id); + res->dbid = host.id; + res->query = req.query.q; + paramlist params; + params.push_back(this); + params.push_back(res); + + char *errmsg = 0; + sqlite3_update_hook(conn, QueryUpdateHook, ¶ms); + if (sqlite3_exec(conn, req.query.q.data(), QueryResult, ¶ms, &errmsg) != SQLITE_OK) + { + std::string error(errmsg); + sqlite3_free(errmsg); + delete[] query; + delete res; + return SQLerror(QSEND_FAIL, error); + } + delete[] query; + + results.push_back(res); + SendNotify(); + return SQLerror(); + } + + static int QueryResult(void *params, int argc, char **argv, char **azColName) + { + paramlist* p = (paramlist*)params; + ((SQLConn*)(*p)[0])->ResultReady(((SQLite3Result*)(*p)[1]), argc, argv, azColName); + return 0; + } + + static void QueryUpdateHook(void *params, int eventid, char const * azSQLite, char const * azColName, sqlite_int64 rowid) + { + paramlist* p = (paramlist*)params; + ((SQLConn*)(*p)[0])->AffectedReady(((SQLite3Result*)(*p)[1])); + } + + void ResultReady(SQLite3Result *res, int cols, char **data, char **colnames) + { + res->AddRow(cols, data, colnames); + } + + void AffectedReady(SQLite3Result *res) + { + res->UpdateAffectedCount(); + } + + int OpenDB() + { + return sqlite3_open(host.host.c_str(), &conn); + } + + void CloseDB() + { + sqlite3_interrupt(conn); + sqlite3_close(conn); + } + + SQLhost GetConfHost() + { + return host; + } + + void SendResults() + { + while (results.size()) + { + SQLite3Result* res = results[0]; + if (res->GetDest()) + { + res->Send(); + } + else + { + /* If the client module is unloaded partway through a query then the provider will set + * the pointer to NULL. We cannot just cancel the query as the result will still come + * through at some point...and it could get messy if we play with invalid pointers... + */ + delete res; + } + results.pop_front(); + } + } + + void ClearResults() + { + while (results.size()) + { + SQLite3Result* res = results[0]; + delete res; + results.pop_front(); + } + } + + void SendNotify() + { + int QueueFD; + if ((QueueFD = socket(AF_FAMILY, SOCK_STREAM, 0)) == -1) + { + /* crap, we're out of sockets... */ + return; + } + + insp_sockaddr addr; + +#ifdef IPV6 + insp_aton("::1", &addr.sin6_addr); + addr.sin6_family = AF_FAMILY; + addr.sin6_port = htons(resultnotify->GetPort()); +#else + insp_inaddr ia; + insp_aton("127.0.0.1", &ia); + addr.sin_family = AF_FAMILY; + addr.sin_addr = ia; + addr.sin_port = htons(resultnotify->GetPort()); +#endif + + if (connect(QueueFD, (sockaddr*)&addr,sizeof(addr)) == -1) + { + /* wtf, we cant connect to it, but we just created it! */ + return; + } + } + +}; + + +class ModuleSQLite3 : public Module +{ + private: + ConnMap connections; + unsigned long currid; + + public: + ModuleSQLite3(InspIRCd* Me) + : Module::Module(Me), currid(0) + { + ServerInstance->UseInterface("SQLutils"); + + if (!ServerInstance->PublishFeature("SQL", this)) + { + throw ModuleException("m_sqlite3: Unable to publish feature 'SQL'"); + } + + resultnotify = new ResultNotifier(ServerInstance, this); + + ReadConf(); + + ServerInstance->PublishInterface("SQL", this); + } + + virtual ~ModuleSQLite3() + { + ClearQueue(); + ClearAllConnections(); + resultnotify->SetFd(-1); + resultnotify->state = I_ERROR; + resultnotify->OnError(I_ERR_SOCKET); + resultnotify->ClosePending = true; + delete resultnotify; + ServerInstance->UnpublishInterface("SQL", this); + ServerInstance->UnpublishFeature("SQL"); + ServerInstance->DoneWithInterface("SQLutils"); + } + + void Implements(char* List) + { + List[I_OnRequest] = List[I_OnRehash] = 1; + } + + void SendQueue() + { + for (ConnMap::iterator iter = connections.begin(); iter != connections.end(); iter++) + { + iter->second->SendResults(); + } + } + + void ClearQueue() + { + for (ConnMap::iterator iter = connections.begin(); iter != connections.end(); iter++) + { + iter->second->ClearResults(); + } + } + + bool HasHost(const SQLhost &host) + { + for (ConnMap::iterator iter = connections.begin(); iter != connections.end(); iter++) + { + if (host == iter->second->GetConfHost()) + return true; + } + return false; + } + + bool HostInConf(const SQLhost &h) + { + ConfigReader conf(ServerInstance); + for(int i = 0; i < conf.Enumerate("database"); i++) + { + SQLhost host; + host.id = conf.ReadValue("database", "id", i); + host.host = conf.ReadValue("database", "hostname", i); + host.port = conf.ReadInteger("database", "port", i, true); + host.name = conf.ReadValue("database", "name", i); + host.user = conf.ReadValue("database", "username", i); + host.pass = conf.ReadValue("database", "password", i); + host.ssl = conf.ReadFlag("database", "ssl", "0", i); + if (h == host) + return true; + } + return false; + } + + void ReadConf() + { + ClearOldConnections(); + + ConfigReader conf(ServerInstance); + for(int i = 0; i < conf.Enumerate("database"); i++) + { + SQLhost host; + + host.id = conf.ReadValue("database", "id", i); + host.host = conf.ReadValue("database", "hostname", i); + host.port = conf.ReadInteger("database", "port", i, true); + host.name = conf.ReadValue("database", "name", i); + host.user = conf.ReadValue("database", "username", i); + host.pass = conf.ReadValue("database", "password", i); + host.ssl = conf.ReadFlag("database", "ssl", "0", i); + + if (HasHost(host)) + continue; + + this->AddConn(host); + } + } + + void AddConn(const SQLhost& hi) + { + if (HasHost(hi)) + { + ServerInstance->Log(DEFAULT, "WARNING: A sqlite connection with id: %s already exists. Aborting database open attempt.", hi.id.c_str()); + return; + } + + SQLConn* newconn; + + newconn = new SQLConn(ServerInstance, this, hi); + + connections.insert(std::make_pair(hi.id, newconn)); + } + + void ClearOldConnections() + { + ConnMap::iterator iter,safei; + for (iter = connections.begin(); iter != connections.end(); iter++) + { + if (!HostInConf(iter->second->GetConfHost())) + { + DELETE(iter->second); + safei = iter; + --iter; + connections.erase(safei); + } + } + } + + void ClearAllConnections() + { + ConnMap::iterator i; + while ((i = connections.begin()) != connections.end()) + { + connections.erase(i); + DELETE(i->second); + } + } + + virtual void OnRehash(userrec* user, const std::string ¶meter) + { + ReadConf(); + } + + virtual char* OnRequest(Request* request) + { + if(strcmp(SQLREQID, request->GetId()) == 0) + { + SQLrequest* req = (SQLrequest*)request; + ConnMap::iterator iter; + if((iter = connections.find(req->dbid)) != connections.end()) + { + req->id = NewID(); + req->error = iter->second->Query(*req); + return SQLSUCCESS; + } + else + { + req->error.Id(BAD_DBID); + return NULL; + } + } + return NULL; + } + + unsigned long NewID() + { + if (currid+1 == 0) + currid++; + + return ++currid; + } + + virtual Version GetVersion() + { + return Version(1,1,0,0,VF_VENDOR|VF_SERVICEPROVIDER,API_VERSION); + } + +}; + +void ResultNotifier::Dispatch() +{ + ((ModuleSQLite3*)mod)->SendQueue(); +} + +MODULE_INIT(ModuleSQLite3); + diff --git a/src/modules/extra/m_sqllog.cpp b/src/modules/extra/m_sqllog.cpp index 04eb1fef1..391e4bbba 100644 --- a/src/modules/extra/m_sqllog.cpp +++ b/src/modules/extra/m_sqllog.cpp @@ -1 +1,310 @@ -/* +------------------------------------+
* | 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 "users.h"
#include "channels.h"
#include "modules.h"
#include "configreader.h"
#include "m_sqlv2.h"
static Module* SQLModule;
static Module* MyMod;
static std::string dbid;
enum LogTypes { LT_OPER = 1, LT_KILL, LT_SERVLINK, LT_XLINE, LT_CONNECT, LT_DISCONNECT, LT_FLOOD, LT_LOADMODULE };
enum QueryState { FIND_SOURCE, FIND_NICK, FIND_HOST, DONE};
class QueryInfo;
std::map<unsigned long,QueryInfo*> active_queries;
class QueryInfo
{
public:
QueryState qs;
unsigned long id;
std::string nick;
std::string source;
std::string hostname;
int sourceid;
int nickid;
int hostid;
int category;
time_t date;
bool insert;
QueryInfo(const std::string &n, const std::string &s, const std::string &h, unsigned long i, int cat)
{
qs = FIND_SOURCE;
nick = n;
source = s;
hostname = h;
id = i;
category = cat;
sourceid = nickid = hostid = -1;
date = time(NULL);
insert = false;
}
void Go(SQLresult* res)
{
SQLrequest req = SQLreq(MyMod, SQLModule, dbid, "", "");
switch (qs)
{
case FIND_SOURCE:
if (res->Rows() && sourceid == -1 && !insert)
{
sourceid = atoi(res->GetValue(0,0).d.c_str());
req = SQLreq(MyMod, SQLModule, dbid, "SELECT id,actor FROM ircd_log_actors WHERE actor='?'", nick);
if(req.Send())
{
insert = false;
qs = FIND_NICK;
active_queries[req.id] = this;
}
}
else if (res->Rows() && sourceid == -1 && insert)
{
req = SQLreq(MyMod, SQLModule, dbid, "SELECT id,actor FROM ircd_log_actors WHERE actor='?'", source);
if(req.Send())
{
insert = false;
qs = FIND_SOURCE;
active_queries[req.id] = this;
}
}
else
{
req = SQLreq(MyMod, SQLModule, dbid, "INSERT INTO ircd_log_actors (actor) VALUES('?')", source);
if(req.Send())
{
insert = true;
qs = FIND_SOURCE;
active_queries[req.id] = this;
}
}
break;
case FIND_NICK:
if (res->Rows() && nickid == -1 && !insert)
{
nickid = atoi(res->GetValue(0,0).d.c_str());
req = SQLreq(MyMod, SQLModule, dbid, "SELECT id,hostname FROM ircd_log_hosts WHERE hostname='?'", hostname);
if(req.Send())
{
insert = false;
qs = FIND_HOST;
active_queries[req.id] = this;
}
}
else if (res->Rows() && nickid == -1 && insert)
{
req = SQLreq(MyMod, SQLModule, dbid, "SELECT id,actor FROM ircd_log_actors WHERE actor='?'", nick);
if(req.Send())
{
insert = false;
qs = FIND_NICK;
active_queries[req.id] = this;
}
}
else
{
req = SQLreq(MyMod, SQLModule, dbid, "INSERT INTO ircd_log_actors (actor) VALUES('?')",nick);
if(req.Send())
{
insert = true;
qs = FIND_NICK;
active_queries[req.id] = this;
}
}
break;
case FIND_HOST:
if (res->Rows() && hostid == -1 && !insert)
{
hostid = atoi(res->GetValue(0,0).d.c_str());
req = SQLreq(MyMod, SQLModule, dbid, "INSERT INTO ircd_log (category_id,nick,host,source,dtime) VALUES("+ConvToStr(category)+","+ConvToStr(nickid)+","+ConvToStr(hostid)+","+ConvToStr(sourceid)+","+ConvToStr(date)+")");
if(req.Send())
{
insert = true;
qs = DONE;
active_queries[req.id] = this;
}
}
else if (res->Rows() && hostid == -1 && insert)
{
req = SQLreq(MyMod, SQLModule, dbid, "SELECT id,hostname FROM ircd_log_hosts WHERE hostname='?'", hostname);
if(req.Send())
{
insert = false;
qs = FIND_HOST;
active_queries[req.id] = this;
}
}
else
{
req = SQLreq(MyMod, SQLModule, dbid, "INSERT INTO ircd_log_hosts (hostname) VALUES('?')", hostname);
if(req.Send())
{
insert = true;
qs = FIND_HOST;
active_queries[req.id] = this;
}
}
break;
case DONE:
delete active_queries[req.id];
active_queries[req.id] = NULL;
break;
}
}
};
/* $ModDesc: Logs network-wide data to an SQL database */
class ModuleSQLLog : public Module
{
ConfigReader* Conf;
public:
ModuleSQLLog(InspIRCd* Me)
: Module::Module(Me)
{
ServerInstance->UseInterface("SQLutils");
ServerInstance->UseInterface("SQL");
Module* SQLutils = ServerInstance->FindModule("m_sqlutils.so");
if (!SQLutils)
throw ModuleException("Can't find m_sqlutils.so. Please load m_sqlutils.so before m_sqlauth.so.");
SQLModule = ServerInstance->FindFeature("SQL");
OnRehash(NULL,"");
MyMod = this;
active_queries.clear();
}
virtual ~ModuleSQLLog()
{
ServerInstance->DoneWithInterface("SQL");
ServerInstance->DoneWithInterface("SQLutils");
}
void Implements(char* List)
{
List[I_OnRehash] = List[I_OnOper] = List[I_OnGlobalOper] = List[I_OnKill] = 1;
List[I_OnPreCommand] = List[I_OnUserConnect] = 1;
List[I_OnUserQuit] = List[I_OnLoadModule] = List[I_OnRequest] = 1;
}
void ReadConfig()
{
ConfigReader Conf(ServerInstance);
dbid = Conf.ReadValue("sqllog","dbid",0); // database id of a database configured in sql module
}
virtual void OnRehash(userrec* user, const std::string ¶meter)
{
ReadConfig();
}
virtual char* OnRequest(Request* request)
{
if(strcmp(SQLRESID, request->GetId()) == 0)
{
SQLresult* res;
std::map<unsigned long, QueryInfo*>::iterator n;
res = static_cast<SQLresult*>(request);
n = active_queries.find(res->id);
if (n != active_queries.end())
{
n->second->Go(res);
std::map<unsigned long, QueryInfo*>::iterator n = active_queries.find(res->id);
active_queries.erase(n);
}
return SQLSUCCESS;
}
return NULL;
}
void AddLogEntry(int category, const std::string &nick, const std::string &host, const std::string &source)
{
// is the sql module loaded? If not, we don't attempt to do anything.
if (!SQLModule)
return;
SQLrequest req = SQLreq(this, SQLModule, dbid, "SELECT id,actor FROM ircd_log_actors WHERE actor='?'", source);
if(req.Send())
{
QueryInfo* i = new QueryInfo(nick, source, host, req.id, category);
i->qs = FIND_SOURCE;
active_queries[req.id] = i;
}
}
virtual void OnOper(userrec* user, const std::string &opertype)
{
AddLogEntry(LT_OPER,user->nick,user->host,user->server);
}
virtual void OnGlobalOper(userrec* user)
{
AddLogEntry(LT_OPER,user->nick,user->host,user->server);
}
virtual int OnKill(userrec* source, userrec* dest, const std::string &reason)
{
AddLogEntry(LT_KILL,dest->nick,dest->host,source->nick);
return 0;
}
virtual int OnPreCommand(const std::string &command, const char** parameters, int pcnt, userrec *user, bool validated, const std::string &original_line)
{
if ((command == "GLINE" || command == "KLINE" || command == "ELINE" || command == "ZLINE") && validated)
{
AddLogEntry(LT_XLINE,user->nick,command[0]+std::string(":")+std::string(parameters[0]),user->server);
}
return 0;
}
virtual void OnUserConnect(userrec* user)
{
AddLogEntry(LT_CONNECT,user->nick,user->host,user->server);
}
virtual void OnUserQuit(userrec* user, const std::string &reason, const std::string &oper_message)
{
AddLogEntry(LT_DISCONNECT,user->nick,user->host,user->server);
}
virtual void OnLoadModule(Module* mod, const std::string &name)
{
AddLogEntry(LT_LOADMODULE,name,ServerInstance->Config->ServerName, ServerInstance->Config->ServerName);
}
virtual Version GetVersion()
{
return Version(1,1,0,1,VF_VENDOR,API_VERSION);
}
};
MODULE_INIT(ModuleSQLLog);
\ No newline at end of file +/* +------------------------------------+ + * | 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 "users.h" +#include "channels.h" +#include "modules.h" +#include "configreader.h" +#include "m_sqlv2.h" + +static Module* SQLModule; +static Module* MyMod; +static std::string dbid; + +enum LogTypes { LT_OPER = 1, LT_KILL, LT_SERVLINK, LT_XLINE, LT_CONNECT, LT_DISCONNECT, LT_FLOOD, LT_LOADMODULE }; + +enum QueryState { FIND_SOURCE, FIND_NICK, FIND_HOST, DONE}; + +class QueryInfo; + +std::map<unsigned long,QueryInfo*> active_queries; + +class QueryInfo +{ +public: + QueryState qs; + unsigned long id; + std::string nick; + std::string source; + std::string hostname; + int sourceid; + int nickid; + int hostid; + int category; + time_t date; + bool insert; + + QueryInfo(const std::string &n, const std::string &s, const std::string &h, unsigned long i, int cat) + { + qs = FIND_SOURCE; + nick = n; + source = s; + hostname = h; + id = i; + category = cat; + sourceid = nickid = hostid = -1; + date = time(NULL); + insert = false; + } + + void Go(SQLresult* res) + { + SQLrequest req = SQLreq(MyMod, SQLModule, dbid, "", ""); + switch (qs) + { + case FIND_SOURCE: + if (res->Rows() && sourceid == -1 && !insert) + { + sourceid = atoi(res->GetValue(0,0).d.c_str()); + req = SQLreq(MyMod, SQLModule, dbid, "SELECT id,actor FROM ircd_log_actors WHERE actor='?'", nick); + if(req.Send()) + { + insert = false; + qs = FIND_NICK; + active_queries[req.id] = this; + } + } + else if (res->Rows() && sourceid == -1 && insert) + { + req = SQLreq(MyMod, SQLModule, dbid, "SELECT id,actor FROM ircd_log_actors WHERE actor='?'", source); + if(req.Send()) + { + insert = false; + qs = FIND_SOURCE; + active_queries[req.id] = this; + } + } + else + { + req = SQLreq(MyMod, SQLModule, dbid, "INSERT INTO ircd_log_actors (actor) VALUES('?')", source); + if(req.Send()) + { + insert = true; + qs = FIND_SOURCE; + active_queries[req.id] = this; + } + } + break; + + case FIND_NICK: + if (res->Rows() && nickid == -1 && !insert) + { + nickid = atoi(res->GetValue(0,0).d.c_str()); + req = SQLreq(MyMod, SQLModule, dbid, "SELECT id,hostname FROM ircd_log_hosts WHERE hostname='?'", hostname); + if(req.Send()) + { + insert = false; + qs = FIND_HOST; + active_queries[req.id] = this; + } + } + else if (res->Rows() && nickid == -1 && insert) + { + req = SQLreq(MyMod, SQLModule, dbid, "SELECT id,actor FROM ircd_log_actors WHERE actor='?'", nick); + if(req.Send()) + { + insert = false; + qs = FIND_NICK; + active_queries[req.id] = this; + } + } + else + { + req = SQLreq(MyMod, SQLModule, dbid, "INSERT INTO ircd_log_actors (actor) VALUES('?')",nick); + if(req.Send()) + { + insert = true; + qs = FIND_NICK; + active_queries[req.id] = this; + } + } + break; + + case FIND_HOST: + if (res->Rows() && hostid == -1 && !insert) + { + hostid = atoi(res->GetValue(0,0).d.c_str()); + req = SQLreq(MyMod, SQLModule, dbid, "INSERT INTO ircd_log (category_id,nick,host,source,dtime) VALUES("+ConvToStr(category)+","+ConvToStr(nickid)+","+ConvToStr(hostid)+","+ConvToStr(sourceid)+","+ConvToStr(date)+")"); + if(req.Send()) + { + insert = true; + qs = DONE; + active_queries[req.id] = this; + } + } + else if (res->Rows() && hostid == -1 && insert) + { + req = SQLreq(MyMod, SQLModule, dbid, "SELECT id,hostname FROM ircd_log_hosts WHERE hostname='?'", hostname); + if(req.Send()) + { + insert = false; + qs = FIND_HOST; + active_queries[req.id] = this; + } + } + else + { + req = SQLreq(MyMod, SQLModule, dbid, "INSERT INTO ircd_log_hosts (hostname) VALUES('?')", hostname); + if(req.Send()) + { + insert = true; + qs = FIND_HOST; + active_queries[req.id] = this; + } + } + break; + + case DONE: + delete active_queries[req.id]; + active_queries[req.id] = NULL; + break; + } + } +}; + +/* $ModDesc: Logs network-wide data to an SQL database */ + +class ModuleSQLLog : public Module +{ + ConfigReader* Conf; + + public: + ModuleSQLLog(InspIRCd* Me) + : Module::Module(Me) + { + ServerInstance->UseInterface("SQLutils"); + ServerInstance->UseInterface("SQL"); + + Module* SQLutils = ServerInstance->FindModule("m_sqlutils.so"); + if (!SQLutils) + throw ModuleException("Can't find m_sqlutils.so. Please load m_sqlutils.so before m_sqlauth.so."); + + SQLModule = ServerInstance->FindFeature("SQL"); + + OnRehash(NULL,""); + MyMod = this; + active_queries.clear(); + } + + virtual ~ModuleSQLLog() + { + ServerInstance->DoneWithInterface("SQL"); + ServerInstance->DoneWithInterface("SQLutils"); + } + + void Implements(char* List) + { + List[I_OnRehash] = List[I_OnOper] = List[I_OnGlobalOper] = List[I_OnKill] = 1; + List[I_OnPreCommand] = List[I_OnUserConnect] = 1; + List[I_OnUserQuit] = List[I_OnLoadModule] = List[I_OnRequest] = 1; + } + + void ReadConfig() + { + ConfigReader Conf(ServerInstance); + dbid = Conf.ReadValue("sqllog","dbid",0); // database id of a database configured in sql module + } + + virtual void OnRehash(userrec* user, const std::string ¶meter) + { + ReadConfig(); + } + + virtual char* OnRequest(Request* request) + { + if(strcmp(SQLRESID, request->GetId()) == 0) + { + SQLresult* res; + std::map<unsigned long, QueryInfo*>::iterator n; + + res = static_cast<SQLresult*>(request); + n = active_queries.find(res->id); + + if (n != active_queries.end()) + { + n->second->Go(res); + std::map<unsigned long, QueryInfo*>::iterator n = active_queries.find(res->id); + active_queries.erase(n); + } + + return SQLSUCCESS; + } + + return NULL; + } + + void AddLogEntry(int category, const std::string &nick, const std::string &host, const std::string &source) + { + // is the sql module loaded? If not, we don't attempt to do anything. + if (!SQLModule) + return; + + SQLrequest req = SQLreq(this, SQLModule, dbid, "SELECT id,actor FROM ircd_log_actors WHERE actor='?'", source); + if(req.Send()) + { + QueryInfo* i = new QueryInfo(nick, source, host, req.id, category); + i->qs = FIND_SOURCE; + active_queries[req.id] = i; + } + } + + virtual void OnOper(userrec* user, const std::string &opertype) + { + AddLogEntry(LT_OPER,user->nick,user->host,user->server); + } + + virtual void OnGlobalOper(userrec* user) + { + AddLogEntry(LT_OPER,user->nick,user->host,user->server); + } + + virtual int OnKill(userrec* source, userrec* dest, const std::string &reason) + { + AddLogEntry(LT_KILL,dest->nick,dest->host,source->nick); + return 0; + } + + virtual int OnPreCommand(const std::string &command, const char** parameters, int pcnt, userrec *user, bool validated, const std::string &original_line) + { + if ((command == "GLINE" || command == "KLINE" || command == "ELINE" || command == "ZLINE") && validated) + { + AddLogEntry(LT_XLINE,user->nick,command[0]+std::string(":")+std::string(parameters[0]),user->server); + } + return 0; + } + + virtual void OnUserConnect(userrec* user) + { + AddLogEntry(LT_CONNECT,user->nick,user->host,user->server); + } + + virtual void OnUserQuit(userrec* user, const std::string &reason, const std::string &oper_message) + { + AddLogEntry(LT_DISCONNECT,user->nick,user->host,user->server); + } + + virtual void OnLoadModule(Module* mod, const std::string &name) + { + AddLogEntry(LT_LOADMODULE,name,ServerInstance->Config->ServerName, ServerInstance->Config->ServerName); + } + + virtual Version GetVersion() + { + return Version(1,1,0,1,VF_VENDOR,API_VERSION); + } + +}; + +MODULE_INIT(ModuleSQLLog); + diff --git a/src/modules/extra/m_sqloper.cpp b/src/modules/extra/m_sqloper.cpp index 4b09ac26e..520869e21 100644 --- a/src/modules/extra/m_sqloper.cpp +++ b/src/modules/extra/m_sqloper.cpp @@ -1 +1,283 @@ -/* +------------------------------------+
* | 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 "users.h"
#include "channels.h"
#include "modules.h"
#include "configreader.h"
#include "m_sqlv2.h"
#include "m_sqlutils.h"
#include "m_hash.h"
#include "commands/cmd_oper.h"
/* $ModDesc: Allows storage of oper credentials in an SQL table */
/* $ModDep: m_sqlv2.h m_sqlutils.h */
class ModuleSQLOper : public Module
{
Module* SQLutils;
Module* HashModule;
std::string databaseid;
public:
ModuleSQLOper(InspIRCd* Me)
: Module::Module(Me)
{
ServerInstance->UseInterface("SQLutils");
ServerInstance->UseInterface("SQL");
ServerInstance->UseInterface("HashRequest");
/* Attempt to locate the md5 service provider, bail if we can't find it */
HashModule = ServerInstance->FindModule("m_md5.so");
if (!HashModule)
throw ModuleException("Can't find m_md5.so. Please load m_md5.so before m_sqloper.so.");
SQLutils = ServerInstance->FindModule("m_sqlutils.so");
if (!SQLutils)
throw ModuleException("Can't find m_sqlutils.so. Please load m_sqlutils.so before m_sqloper.so.");
OnRehash(NULL,"");
}
virtual ~ModuleSQLOper()
{
ServerInstance->DoneWithInterface("SQL");
ServerInstance->DoneWithInterface("SQLutils");
ServerInstance->DoneWithInterface("HashRequest");
}
void Implements(char* List)
{
List[I_OnRequest] = List[I_OnRehash] = List[I_OnPreCommand] = 1;
}
virtual void OnRehash(userrec* user, const std::string ¶meter)
{
ConfigReader Conf(ServerInstance);
databaseid = Conf.ReadValue("sqloper", "dbid", 0); /* Database ID of a database configured for the service provider module */
}
virtual int OnPreCommand(const std::string &command, const char** parameters, int pcnt, userrec *user, bool validated, const std::string &original_line)
{
if ((validated) && (command == "OPER"))
{
if (LookupOper(user, parameters[0], parameters[1]))
{
/* Returning true here just means the query is in progress, or on it's way to being
* in progress. Nothing about the /oper actually being successful..
* If the oper lookup fails later, we pass the command to the original handler
* for /oper by calling its Handle method directly.
*/
return 1;
}
}
return 0;
}
bool LookupOper(userrec* user, const std::string &username, const std::string &password)
{
Module* target;
target = ServerInstance->FindFeature("SQL");
if (target)
{
/* Reset hash module first back to MD5 standard state */
HashResetRequest(this, HashModule).Send();
/* Make an MD5 hash of the password for using in the query */
std::string md5_pass_hash = HashSumRequest(this, HashModule, password.c_str()).Send();
/* We generate our own MD5 sum here because some database providers (e.g. SQLite) dont have a builtin md5 function,
* also hashing it in the module and only passing a remote query containing a hash is more secure.
*/
SQLrequest req = SQLreq(this, target, databaseid, "SELECT username, password, hostname, type FROM ircd_opers WHERE username = '?' AND password='?'", username, md5_pass_hash);
if (req.Send())
{
/* When we get the query response from the service provider we will be given an ID to play with,
* just an ID number which is unique to this query. We need a way of associating that ID with a userrec
* so we insert it into a map mapping the IDs to users.
* Thankfully m_sqlutils provides this, it will associate a ID with a user or channel, and if the user quits it removes the
* association. This means that if the user quits during a query we will just get a failed lookup from m_sqlutils - telling
* us to discard the query.
*/
AssociateUser(this, SQLutils, req.id, user).Send();
user->Extend("oper_user", strdup(username.c_str()));
user->Extend("oper_pass", strdup(password.c_str()));
return true;
}
else
{
return false;
}
}
else
{
ServerInstance->Log(SPARSE, "WARNING: Couldn't find SQL provider module. NOBODY will be able to oper up unless their o:line is statically configured");
return false;
}
}
virtual char* OnRequest(Request* request)
{
if (strcmp(SQLRESID, request->GetId()) == 0)
{
SQLresult* res = static_cast<SQLresult*>(request);
userrec* user = GetAssocUser(this, SQLutils, res->id).S().user;
UnAssociate(this, SQLutils, res->id).S();
char* tried_user = NULL;
char* tried_pass = NULL;
user->GetExt("oper_user", tried_user);
user->GetExt("oper_pass", tried_pass);
if (user)
{
if (res->error.Id() == NO_ERROR)
{
if (res->Rows())
{
/* We got a row in the result, this means there was a record for the oper..
* now we just need to check if their host matches, and if it does then
* oper them up.
*
* We now (previous versions of the module didn't) support multiple SQL
* rows per-oper in the same way the config file does, all rows will be tried
* until one is found which matches. This is useful to define several different
* hosts for a single oper.
*
* The for() loop works as SQLresult::GetRowMap() returns an empty map when there
* are no more rows to return.
*/
for (SQLfieldMap& row = res->GetRowMap(); row.size(); row = res->GetRowMap())
{
if (OperUser(user, row["username"].d, row["password"].d, row["hostname"].d, row["type"].d))
{
/* If/when one of the rows matches, stop checking and return */
return SQLSUCCESS;
}
if (tried_user && tried_pass)
{
LoginFail(user, tried_user, tried_pass);
free(tried_user);
free(tried_pass);
user->Shrink("oper_user");
user->Shrink("oper_pass");
}
}
}
else
{
/* No rows in result, this means there was no oper line for the user,
* we should have already checked the o:lines so now we need an
* "insufficient awesomeness" (invalid credentials) error
*/
if (tried_user && tried_pass)
{
LoginFail(user, tried_user, tried_pass);
free(tried_user);
free(tried_pass);
user->Shrink("oper_user");
user->Shrink("oper_pass");
}
}
}
else
{
/* This one shouldn't happen, the query failed for some reason.
* We have to fail the /oper request and give them the same error
* as above.
*/
if (tried_user && tried_pass)
{
LoginFail(user, tried_user, tried_pass);
free(tried_user);
free(tried_pass);
user->Shrink("oper_user");
user->Shrink("oper_pass");
}
}
}
return SQLSUCCESS;
}
return NULL;
}
void LoginFail(userrec* user, const std::string &username, const std::string &pass)
{
command_t* oper_command = ServerInstance->Parser->GetHandler("OPER");
if (oper_command)
{
const char* params[] = { username.c_str(), pass.c_str() };
oper_command->Handle(params, 2, user);
}
else
{
ServerInstance->Log(DEBUG, "BUG: WHAT?! Why do we have no OPER command?!");
}
}
bool OperUser(userrec* user, const std::string &username, const std::string &password, const std::string &pattern, const std::string &type)
{
ConfigReader Conf(ServerInstance);
for (int j = 0; j < Conf.Enumerate("type"); j++)
{
std::string tname = Conf.ReadValue("type","name",j);
std::string hostname(user->ident);
hostname.append("@").append(user->host);
if ((tname == type) && OneOfMatches(hostname.c_str(), user->GetIPString(), pattern.c_str()))
{
/* Opertype and host match, looks like this is it. */
std::string operhost = Conf.ReadValue("type", "host", j);
if (operhost.size())
user->ChangeDisplayedHost(operhost.c_str());
ServerInstance->SNO->WriteToSnoMask('o',"%s (%s@%s) is now an IRC operator of type %s", user->nick, user->ident, user->host, type.c_str());
user->WriteServ("381 %s :You are now an IRC operator of type %s", user->nick, type.c_str());
if (!user->modes[UM_OPERATOR])
user->Oper(type);
return true;
}
}
return false;
}
virtual Version GetVersion()
{
return Version(1,1,1,0,VF_VENDOR,API_VERSION);
}
};
MODULE_INIT(ModuleSQLOper);
\ No newline at end of file +/* +------------------------------------+ + * | 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 "users.h" +#include "channels.h" +#include "modules.h" +#include "configreader.h" + +#include "m_sqlv2.h" +#include "m_sqlutils.h" +#include "m_hash.h" +#include "commands/cmd_oper.h" + +/* $ModDesc: Allows storage of oper credentials in an SQL table */ +/* $ModDep: m_sqlv2.h m_sqlutils.h */ + +class ModuleSQLOper : public Module +{ + Module* SQLutils; + Module* HashModule; + std::string databaseid; + +public: + ModuleSQLOper(InspIRCd* Me) + : Module::Module(Me) + { + ServerInstance->UseInterface("SQLutils"); + ServerInstance->UseInterface("SQL"); + ServerInstance->UseInterface("HashRequest"); + + /* Attempt to locate the md5 service provider, bail if we can't find it */ + HashModule = ServerInstance->FindModule("m_md5.so"); + if (!HashModule) + throw ModuleException("Can't find m_md5.so. Please load m_md5.so before m_sqloper.so."); + + SQLutils = ServerInstance->FindModule("m_sqlutils.so"); + if (!SQLutils) + throw ModuleException("Can't find m_sqlutils.so. Please load m_sqlutils.so before m_sqloper.so."); + + OnRehash(NULL,""); + } + + virtual ~ModuleSQLOper() + { + ServerInstance->DoneWithInterface("SQL"); + ServerInstance->DoneWithInterface("SQLutils"); + ServerInstance->DoneWithInterface("HashRequest"); + } + + void Implements(char* List) + { + List[I_OnRequest] = List[I_OnRehash] = List[I_OnPreCommand] = 1; + } + + virtual void OnRehash(userrec* user, const std::string ¶meter) + { + ConfigReader Conf(ServerInstance); + + databaseid = Conf.ReadValue("sqloper", "dbid", 0); /* Database ID of a database configured for the service provider module */ + } + + virtual int OnPreCommand(const std::string &command, const char** parameters, int pcnt, userrec *user, bool validated, const std::string &original_line) + { + if ((validated) && (command == "OPER")) + { + if (LookupOper(user, parameters[0], parameters[1])) + { + /* Returning true here just means the query is in progress, or on it's way to being + * in progress. Nothing about the /oper actually being successful.. + * If the oper lookup fails later, we pass the command to the original handler + * for /oper by calling its Handle method directly. + */ + return 1; + } + } + return 0; + } + + bool LookupOper(userrec* user, const std::string &username, const std::string &password) + { + Module* target; + + target = ServerInstance->FindFeature("SQL"); + + if (target) + { + /* Reset hash module first back to MD5 standard state */ + HashResetRequest(this, HashModule).Send(); + /* Make an MD5 hash of the password for using in the query */ + std::string md5_pass_hash = HashSumRequest(this, HashModule, password.c_str()).Send(); + + /* We generate our own MD5 sum here because some database providers (e.g. SQLite) dont have a builtin md5 function, + * also hashing it in the module and only passing a remote query containing a hash is more secure. + */ + + SQLrequest req = SQLreq(this, target, databaseid, "SELECT username, password, hostname, type FROM ircd_opers WHERE username = '?' AND password='?'", username, md5_pass_hash); + + if (req.Send()) + { + /* When we get the query response from the service provider we will be given an ID to play with, + * just an ID number which is unique to this query. We need a way of associating that ID with a userrec + * so we insert it into a map mapping the IDs to users. + * Thankfully m_sqlutils provides this, it will associate a ID with a user or channel, and if the user quits it removes the + * association. This means that if the user quits during a query we will just get a failed lookup from m_sqlutils - telling + * us to discard the query. + */ + AssociateUser(this, SQLutils, req.id, user).Send(); + + user->Extend("oper_user", strdup(username.c_str())); + user->Extend("oper_pass", strdup(password.c_str())); + + return true; + } + else + { + return false; + } + } + else + { + ServerInstance->Log(SPARSE, "WARNING: Couldn't find SQL provider module. NOBODY will be able to oper up unless their o:line is statically configured"); + return false; + } + } + + virtual char* OnRequest(Request* request) + { + if (strcmp(SQLRESID, request->GetId()) == 0) + { + SQLresult* res = static_cast<SQLresult*>(request); + + userrec* user = GetAssocUser(this, SQLutils, res->id).S().user; + UnAssociate(this, SQLutils, res->id).S(); + + char* tried_user = NULL; + char* tried_pass = NULL; + + user->GetExt("oper_user", tried_user); + user->GetExt("oper_pass", tried_pass); + + if (user) + { + if (res->error.Id() == NO_ERROR) + { + if (res->Rows()) + { + /* We got a row in the result, this means there was a record for the oper.. + * now we just need to check if their host matches, and if it does then + * oper them up. + * + * We now (previous versions of the module didn't) support multiple SQL + * rows per-oper in the same way the config file does, all rows will be tried + * until one is found which matches. This is useful to define several different + * hosts for a single oper. + * + * The for() loop works as SQLresult::GetRowMap() returns an empty map when there + * are no more rows to return. + */ + + for (SQLfieldMap& row = res->GetRowMap(); row.size(); row = res->GetRowMap()) + { + if (OperUser(user, row["username"].d, row["password"].d, row["hostname"].d, row["type"].d)) + { + /* If/when one of the rows matches, stop checking and return */ + return SQLSUCCESS; + } + if (tried_user && tried_pass) + { + LoginFail(user, tried_user, tried_pass); + free(tried_user); + free(tried_pass); + user->Shrink("oper_user"); + user->Shrink("oper_pass"); + } + } + } + else + { + /* No rows in result, this means there was no oper line for the user, + * we should have already checked the o:lines so now we need an + * "insufficient awesomeness" (invalid credentials) error + */ + if (tried_user && tried_pass) + { + LoginFail(user, tried_user, tried_pass); + free(tried_user); + free(tried_pass); + user->Shrink("oper_user"); + user->Shrink("oper_pass"); + } + } + } + else + { + /* This one shouldn't happen, the query failed for some reason. + * We have to fail the /oper request and give them the same error + * as above. + */ + if (tried_user && tried_pass) + { + LoginFail(user, tried_user, tried_pass); + free(tried_user); + free(tried_pass); + user->Shrink("oper_user"); + user->Shrink("oper_pass"); + } + + } + } + + return SQLSUCCESS; + } + + return NULL; + } + + void LoginFail(userrec* user, const std::string &username, const std::string &pass) + { + command_t* oper_command = ServerInstance->Parser->GetHandler("OPER"); + + if (oper_command) + { + const char* params[] = { username.c_str(), pass.c_str() }; + oper_command->Handle(params, 2, user); + } + else + { + ServerInstance->Log(DEBUG, "BUG: WHAT?! Why do we have no OPER command?!"); + } + } + + bool OperUser(userrec* user, const std::string &username, const std::string &password, const std::string &pattern, const std::string &type) + { + ConfigReader Conf(ServerInstance); + + for (int j = 0; j < Conf.Enumerate("type"); j++) + { + std::string tname = Conf.ReadValue("type","name",j); + std::string hostname(user->ident); + + hostname.append("@").append(user->host); + + if ((tname == type) && OneOfMatches(hostname.c_str(), user->GetIPString(), pattern.c_str())) + { + /* Opertype and host match, looks like this is it. */ + std::string operhost = Conf.ReadValue("type", "host", j); + + if (operhost.size()) + user->ChangeDisplayedHost(operhost.c_str()); + + ServerInstance->SNO->WriteToSnoMask('o',"%s (%s@%s) is now an IRC operator of type %s", user->nick, user->ident, user->host, type.c_str()); + user->WriteServ("381 %s :You are now an IRC operator of type %s", user->nick, type.c_str()); + + if (!user->modes[UM_OPERATOR]) + user->Oper(type); + + return true; + } + } + + return false; + } + + virtual Version GetVersion() + { + return Version(1,1,1,0,VF_VENDOR,API_VERSION); + } + +}; + +MODULE_INIT(ModuleSQLOper); + diff --git a/src/modules/extra/m_sqlutils.cpp b/src/modules/extra/m_sqlutils.cpp index 6cd09252b..b470f99af 100644 --- a/src/modules/extra/m_sqlutils.cpp +++ b/src/modules/extra/m_sqlutils.cpp @@ -1 +1,238 @@ -/* +------------------------------------+
* | 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 <sstream>
#include <list>
#include "users.h"
#include "channels.h"
#include "modules.h"
#include "configreader.h"
#include "m_sqlutils.h"
/* $ModDesc: Provides some utilities to SQL client modules, such as mapping queries to users and channels */
/* $ModDep: m_sqlutils.h */
typedef std::map<unsigned long, userrec*> IdUserMap;
typedef std::map<unsigned long, chanrec*> IdChanMap;
typedef std::list<unsigned long> AssocIdList;
class ModuleSQLutils : public Module
{
private:
IdUserMap iduser;
IdChanMap idchan;
public:
ModuleSQLutils(InspIRCd* Me)
: Module::Module(Me)
{
ServerInstance->PublishInterface("SQLutils", this);
}
virtual ~ModuleSQLutils()
{
ServerInstance->UnpublishInterface("SQLutils", this);
}
void Implements(char* List)
{
List[I_OnChannelDelete] = List[I_OnUnloadModule] = List[I_OnRequest] = List[I_OnUserDisconnect] = 1;
}
virtual char* OnRequest(Request* request)
{
if(strcmp(SQLUTILAU, request->GetId()) == 0)
{
AssociateUser* req = (AssociateUser*)request;
iduser.insert(std::make_pair(req->id, req->user));
AttachList(req->user, req->id);
}
else if(strcmp(SQLUTILAC, request->GetId()) == 0)
{
AssociateChan* req = (AssociateChan*)request;
idchan.insert(std::make_pair(req->id, req->chan));
AttachList(req->chan, req->id);
}
else if(strcmp(SQLUTILUA, request->GetId()) == 0)
{
UnAssociate* req = (UnAssociate*)request;
/* Unassociate a given query ID with all users and channels
* it is associated with.
*/
DoUnAssociate(iduser, req->id);
DoUnAssociate(idchan, req->id);
}
else if(strcmp(SQLUTILGU, request->GetId()) == 0)
{
GetAssocUser* req = (GetAssocUser*)request;
IdUserMap::iterator iter = iduser.find(req->id);
if(iter != iduser.end())
{
req->user = iter->second;
}
}
else if(strcmp(SQLUTILGC, request->GetId()) == 0)
{
GetAssocChan* req = (GetAssocChan*)request;
IdChanMap::iterator iter = idchan.find(req->id);
if(iter != idchan.end())
{
req->chan = iter->second;
}
}
return SQLUTILSUCCESS;
}
virtual void OnUserDisconnect(userrec* user)
{
/* A user is disconnecting, first we need to check if they have a list of queries associated with them.
* Then, if they do, we need to erase each of them from our IdUserMap (iduser) so when the module that
* associated them asks to look them up then it gets a NULL result and knows to discard the query.
*/
AssocIdList* il;
if(user->GetExt("sqlutils_queryids", il))
{
for(AssocIdList::iterator listiter = il->begin(); listiter != il->end(); listiter++)
{
IdUserMap::iterator iter;
iter = iduser.find(*listiter);
if(iter != iduser.end())
{
if(iter->second != user)
{
ServerInstance->Log(DEBUG, "BUG: ID associated with user %s doesn't have the same userrec* associated with it in the map (erasing anyway)", user->nick);
}
iduser.erase(iter);
}
else
{
ServerInstance->Log(DEBUG, "BUG: user %s was extended with sqlutils_queryids but there was nothing matching in the map", user->nick);
}
}
user->Shrink("sqlutils_queryids");
delete il;
}
}
void AttachList(Extensible* obj, unsigned long id)
{
AssocIdList* il;
if(!obj->GetExt("sqlutils_queryids", il))
{
/* Doesn't already exist, create a new list and attach it. */
il = new AssocIdList;
obj->Extend("sqlutils_queryids", il);
}
/* Now either way we have a valid list in il, attached. */
il->push_back(id);
}
void RemoveFromList(Extensible* obj, unsigned long id)
{
AssocIdList* il;
if(obj->GetExt("sqlutils_queryids", il))
{
/* Only do anything if the list exists... (which it ought to) */
il->remove(id);
if(il->empty())
{
/* If we just emptied it.. */
delete il;
obj->Shrink("sqlutils_queryids");
}
}
}
template <class T> void DoUnAssociate(T &map, unsigned long id)
{
/* For each occurence of 'id' (well, only one..it's not a multimap) in 'map'
* remove it from the map, take an Extensible* value from the map and remove
* 'id' from the list of query IDs attached to it.
*/
typename T::iterator iter = map.find(id);
if(iter != map.end())
{
/* Found a value indexed by 'id', call RemoveFromList()
* on it with 'id' to remove 'id' from the list attached
* to the value.
*/
RemoveFromList(iter->second, id);
}
}
virtual void OnChannelDelete(chanrec* chan)
{
/* A channel is being destroyed, first we need to check if it has a list of queries associated with it.
* Then, if it does, we need to erase each of them from our IdChanMap (idchan) so when the module that
* associated them asks to look them up then it gets a NULL result and knows to discard the query.
*/
AssocIdList* il;
if(chan->GetExt("sqlutils_queryids", il))
{
for(AssocIdList::iterator listiter = il->begin(); listiter != il->end(); listiter++)
{
IdChanMap::iterator iter;
iter = idchan.find(*listiter);
if(iter != idchan.end())
{
if(iter->second != chan)
{
ServerInstance->Log(DEBUG, "BUG: ID associated with channel %s doesn't have the same chanrec* associated with it in the map (erasing anyway)", chan->name);
}
idchan.erase(iter);
}
else
{
ServerInstance->Log(DEBUG, "BUG: channel %s was extended with sqlutils_queryids but there was nothing matching in the map", chan->name);
}
}
chan->Shrink("sqlutils_queryids");
delete il;
}
}
virtual Version GetVersion()
{
return Version(1, 1, 0, 0, VF_VENDOR|VF_SERVICEPROVIDER, API_VERSION);
}
};
MODULE_INIT(ModuleSQLutils);
\ No newline at end of file +/* +------------------------------------+ + * | 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 <sstream> +#include <list> +#include "users.h" +#include "channels.h" +#include "modules.h" +#include "configreader.h" +#include "m_sqlutils.h" + +/* $ModDesc: Provides some utilities to SQL client modules, such as mapping queries to users and channels */ +/* $ModDep: m_sqlutils.h */ + +typedef std::map<unsigned long, userrec*> IdUserMap; +typedef std::map<unsigned long, chanrec*> IdChanMap; +typedef std::list<unsigned long> AssocIdList; + +class ModuleSQLutils : public Module +{ +private: + IdUserMap iduser; + IdChanMap idchan; + +public: + ModuleSQLutils(InspIRCd* Me) + : Module::Module(Me) + { + ServerInstance->PublishInterface("SQLutils", this); + } + + virtual ~ModuleSQLutils() + { + ServerInstance->UnpublishInterface("SQLutils", this); + } + + void Implements(char* List) + { + List[I_OnChannelDelete] = List[I_OnUnloadModule] = List[I_OnRequest] = List[I_OnUserDisconnect] = 1; + } + + virtual char* OnRequest(Request* request) + { + if(strcmp(SQLUTILAU, request->GetId()) == 0) + { + AssociateUser* req = (AssociateUser*)request; + + iduser.insert(std::make_pair(req->id, req->user)); + + AttachList(req->user, req->id); + } + else if(strcmp(SQLUTILAC, request->GetId()) == 0) + { + AssociateChan* req = (AssociateChan*)request; + + idchan.insert(std::make_pair(req->id, req->chan)); + + AttachList(req->chan, req->id); + } + else if(strcmp(SQLUTILUA, request->GetId()) == 0) + { + UnAssociate* req = (UnAssociate*)request; + + /* Unassociate a given query ID with all users and channels + * it is associated with. + */ + + DoUnAssociate(iduser, req->id); + DoUnAssociate(idchan, req->id); + } + else if(strcmp(SQLUTILGU, request->GetId()) == 0) + { + GetAssocUser* req = (GetAssocUser*)request; + + IdUserMap::iterator iter = iduser.find(req->id); + + if(iter != iduser.end()) + { + req->user = iter->second; + } + } + else if(strcmp(SQLUTILGC, request->GetId()) == 0) + { + GetAssocChan* req = (GetAssocChan*)request; + + IdChanMap::iterator iter = idchan.find(req->id); + + if(iter != idchan.end()) + { + req->chan = iter->second; + } + } + + return SQLUTILSUCCESS; + } + + virtual void OnUserDisconnect(userrec* user) + { + /* A user is disconnecting, first we need to check if they have a list of queries associated with them. + * Then, if they do, we need to erase each of them from our IdUserMap (iduser) so when the module that + * associated them asks to look them up then it gets a NULL result and knows to discard the query. + */ + AssocIdList* il; + + if(user->GetExt("sqlutils_queryids", il)) + { + for(AssocIdList::iterator listiter = il->begin(); listiter != il->end(); listiter++) + { + IdUserMap::iterator iter; + + iter = iduser.find(*listiter); + + if(iter != iduser.end()) + { + if(iter->second != user) + { + ServerInstance->Log(DEBUG, "BUG: ID associated with user %s doesn't have the same userrec* associated with it in the map (erasing anyway)", user->nick); + } + + iduser.erase(iter); + } + else + { + ServerInstance->Log(DEBUG, "BUG: user %s was extended with sqlutils_queryids but there was nothing matching in the map", user->nick); + } + } + + user->Shrink("sqlutils_queryids"); + delete il; + } + } + + void AttachList(Extensible* obj, unsigned long id) + { + AssocIdList* il; + + if(!obj->GetExt("sqlutils_queryids", il)) + { + /* Doesn't already exist, create a new list and attach it. */ + il = new AssocIdList; + obj->Extend("sqlutils_queryids", il); + } + + /* Now either way we have a valid list in il, attached. */ + il->push_back(id); + } + + void RemoveFromList(Extensible* obj, unsigned long id) + { + AssocIdList* il; + + if(obj->GetExt("sqlutils_queryids", il)) + { + /* Only do anything if the list exists... (which it ought to) */ + il->remove(id); + + if(il->empty()) + { + /* If we just emptied it.. */ + delete il; + obj->Shrink("sqlutils_queryids"); + } + } + } + + template <class T> void DoUnAssociate(T &map, unsigned long id) + { + /* For each occurence of 'id' (well, only one..it's not a multimap) in 'map' + * remove it from the map, take an Extensible* value from the map and remove + * 'id' from the list of query IDs attached to it. + */ + typename T::iterator iter = map.find(id); + + if(iter != map.end()) + { + /* Found a value indexed by 'id', call RemoveFromList() + * on it with 'id' to remove 'id' from the list attached + * to the value. + */ + RemoveFromList(iter->second, id); + } + } + + virtual void OnChannelDelete(chanrec* chan) + { + /* A channel is being destroyed, first we need to check if it has a list of queries associated with it. + * Then, if it does, we need to erase each of them from our IdChanMap (idchan) so when the module that + * associated them asks to look them up then it gets a NULL result and knows to discard the query. + */ + AssocIdList* il; + + if(chan->GetExt("sqlutils_queryids", il)) + { + for(AssocIdList::iterator listiter = il->begin(); listiter != il->end(); listiter++) + { + IdChanMap::iterator iter; + + iter = idchan.find(*listiter); + + if(iter != idchan.end()) + { + if(iter->second != chan) + { + ServerInstance->Log(DEBUG, "BUG: ID associated with channel %s doesn't have the same chanrec* associated with it in the map (erasing anyway)", chan->name); + } + idchan.erase(iter); + } + else + { + ServerInstance->Log(DEBUG, "BUG: channel %s was extended with sqlutils_queryids but there was nothing matching in the map", chan->name); + } + } + + chan->Shrink("sqlutils_queryids"); + delete il; + } + } + + virtual Version GetVersion() + { + return Version(1, 1, 0, 0, VF_VENDOR|VF_SERVICEPROVIDER, API_VERSION); + } + +}; + +MODULE_INIT(ModuleSQLutils); + diff --git a/src/modules/extra/m_sqlutils.h b/src/modules/extra/m_sqlutils.h index cdde51f67..92fbdf5c7 100644 --- a/src/modules/extra/m_sqlutils.h +++ b/src/modules/extra/m_sqlutils.h @@ -1 +1,143 @@ -/* +------------------------------------+
* | 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.
*
* ---------------------------------------------------
*/
#ifndef INSPIRCD_SQLUTILS
#define INSPIRCD_SQLUTILS
#include "modules.h"
#define SQLUTILAU "SQLutil AssociateUser"
#define SQLUTILAC "SQLutil AssociateChan"
#define SQLUTILUA "SQLutil UnAssociate"
#define SQLUTILGU "SQLutil GetAssocUser"
#define SQLUTILGC "SQLutil GetAssocChan"
#define SQLUTILSUCCESS "You shouldn't be reading this (success)"
/** Used to associate an SQL query with a user
*/
class AssociateUser : public Request
{
public:
/** Query ID
*/
unsigned long id;
/** User
*/
userrec* user;
AssociateUser(Module* s, Module* d, unsigned long i, userrec* u)
: Request(s, d, SQLUTILAU), id(i), user(u)
{
}
AssociateUser& S()
{
Send();
return *this;
}
};
/** Used to associate an SQL query with a channel
*/
class AssociateChan : public Request
{
public:
/** Query ID
*/
unsigned long id;
/** Channel
*/
chanrec* chan;
AssociateChan(Module* s, Module* d, unsigned long i, chanrec* u)
: Request(s, d, SQLUTILAC), id(i), chan(u)
{
}
AssociateChan& S()
{
Send();
return *this;
}
};
/** Unassociate a user or class from an SQL query
*/
class UnAssociate : public Request
{
public:
/** The query ID
*/
unsigned long id;
UnAssociate(Module* s, Module* d, unsigned long i)
: Request(s, d, SQLUTILUA), id(i)
{
}
UnAssociate& S()
{
Send();
return *this;
}
};
/** Get the user associated with an SQL query ID
*/
class GetAssocUser : public Request
{
public:
/** The query id
*/
unsigned long id;
/** The user
*/
userrec* user;
GetAssocUser(Module* s, Module* d, unsigned long i)
: Request(s, d, SQLUTILGU), id(i), user(NULL)
{
}
GetAssocUser& S()
{
Send();
return *this;
}
};
/** Get the channel associated with an SQL query ID
*/
class GetAssocChan : public Request
{
public:
/** The query id
*/
unsigned long id;
/** The channel
*/
chanrec* chan;
GetAssocChan(Module* s, Module* d, unsigned long i)
: Request(s, d, SQLUTILGC), id(i), chan(NULL)
{
}
GetAssocChan& S()
{
Send();
return *this;
}
};
#endif
\ No newline at end of file +/* +------------------------------------+ + * | 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. + * + * --------------------------------------------------- + */ + +#ifndef INSPIRCD_SQLUTILS +#define INSPIRCD_SQLUTILS + +#include "modules.h" + +#define SQLUTILAU "SQLutil AssociateUser" +#define SQLUTILAC "SQLutil AssociateChan" +#define SQLUTILUA "SQLutil UnAssociate" +#define SQLUTILGU "SQLutil GetAssocUser" +#define SQLUTILGC "SQLutil GetAssocChan" +#define SQLUTILSUCCESS "You shouldn't be reading this (success)" + +/** Used to associate an SQL query with a user + */ +class AssociateUser : public Request +{ +public: + /** Query ID + */ + unsigned long id; + /** User + */ + userrec* user; + + AssociateUser(Module* s, Module* d, unsigned long i, userrec* u) + : Request(s, d, SQLUTILAU), id(i), user(u) + { + } + + AssociateUser& S() + { + Send(); + return *this; + } +}; + +/** Used to associate an SQL query with a channel + */ +class AssociateChan : public Request +{ +public: + /** Query ID + */ + unsigned long id; + /** Channel + */ + chanrec* chan; + + AssociateChan(Module* s, Module* d, unsigned long i, chanrec* u) + : Request(s, d, SQLUTILAC), id(i), chan(u) + { + } + + AssociateChan& S() + { + Send(); + return *this; + } +}; + +/** Unassociate a user or class from an SQL query + */ +class UnAssociate : public Request +{ +public: + /** The query ID + */ + unsigned long id; + + UnAssociate(Module* s, Module* d, unsigned long i) + : Request(s, d, SQLUTILUA), id(i) + { + } + + UnAssociate& S() + { + Send(); + return *this; + } +}; + +/** Get the user associated with an SQL query ID + */ +class GetAssocUser : public Request +{ +public: + /** The query id + */ + unsigned long id; + /** The user + */ + userrec* user; + + GetAssocUser(Module* s, Module* d, unsigned long i) + : Request(s, d, SQLUTILGU), id(i), user(NULL) + { + } + + GetAssocUser& S() + { + Send(); + return *this; + } +}; + +/** Get the channel associated with an SQL query ID + */ +class GetAssocChan : public Request +{ +public: + /** The query id + */ + unsigned long id; + /** The channel + */ + chanrec* chan; + + GetAssocChan(Module* s, Module* d, unsigned long i) + : Request(s, d, SQLUTILGC), id(i), chan(NULL) + { + } + + GetAssocChan& S() + { + Send(); + return *this; + } +}; + +#endif diff --git a/src/modules/extra/m_sqlv2.h b/src/modules/extra/m_sqlv2.h index decac4b57..c7f6edbb9 100644 --- a/src/modules/extra/m_sqlv2.h +++ b/src/modules/extra/m_sqlv2.h @@ -1 +1,605 @@ -/* +------------------------------------+
* | 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.
*
* ---------------------------------------------------
*/
#ifndef INSPIRCD_SQLAPI_2
#define INSPIRCD_SQLAPI_2
#include <string>
#include <deque>
#include <map>
#include "modules.h"
/** SQLreq define.
* This is the voodoo magic which lets us pass multiple
* parameters to the SQLrequest constructor... voodoo...
*/
#define SQLreq(a, b, c, d, e...) SQLrequest(a, b, c, (SQLquery(d), ##e))
/** Identifiers used to identify Request types
*/
#define SQLREQID "SQLv2 Request"
#define SQLRESID "SQLv2 Result"
#define SQLSUCCESS "You shouldn't be reading this (success)"
/** Defines the error types which SQLerror may be set to
*/
enum SQLerrorNum { NO_ERROR, BAD_DBID, BAD_CONN, QSEND_FAIL, QREPLY_FAIL };
/** A list of format parameters for an SQLquery object.
*/
typedef std::deque<std::string> ParamL;
/** The base class of SQL exceptions
*/
class SQLexception : public ModuleException
{
public:
SQLexception(const std::string &reason) : ModuleException(reason)
{
}
SQLexception() : ModuleException("SQLv2: Undefined exception")
{
}
};
/** An exception thrown when a bad column or row name or id is requested
*/
class SQLbadColName : public SQLexception
{
public:
SQLbadColName() : SQLexception("SQLv2: Bad column name")
{
}
};
/** SQLerror holds the error state of any SQLrequest or SQLresult.
* The error string varies from database software to database software
* and should be used to display informational error messages to users.
*/
class SQLerror : public classbase
{
/** The error id
*/
SQLerrorNum id;
/** The error string
*/
std::string str;
public:
/** Initialize an SQLerror
* @param i The error ID to set
* @param s The (optional) error string to set
*/
SQLerror(SQLerrorNum i = NO_ERROR, const std::string &s = "")
: id(i), str(s)
{
}
/** Return the ID of the error
*/
SQLerrorNum Id()
{
return id;
}
/** Set the ID of an error
* @param i The new error ID to set
* @return the ID which was set
*/
SQLerrorNum Id(SQLerrorNum i)
{
id = i;
return id;
}
/** Set the error string for an error
* @param s The new error string to set
*/
void Str(const std::string &s)
{
str = s;
}
/** Return the error string for an error
*/
const char* Str()
{
if(str.length())
return str.c_str();
switch(id)
{
case NO_ERROR:
return "No error";
case BAD_DBID:
return "Invalid database ID";
case BAD_CONN:
return "Invalid connection";
case QSEND_FAIL:
return "Sending query failed";
case QREPLY_FAIL:
return "Getting query result failed";
default:
return "Unknown error";
}
}
};
/** SQLquery provides a way to represent a query string, and its parameters in a type-safe way.
* C++ has no native type-safe way of having a variable number of arguments to a function,
* the workaround for this isn't easy to describe simply, but in a nutshell what's really
* happening when - from the above example - you do this:
*
* SQLrequest foo = SQLreq(this, target, "databaseid", "SELECT (foo, bar) FROM rawr WHERE foo = '?' AND bar = ?", "Hello", "42");
*
* what's actually happening is functionally this:
*
* SQLrequest foo = SQLreq(this, target, "databaseid", query("SELECT (foo, bar) FROM rawr WHERE foo = '?' AND bar = ?").addparam("Hello").addparam("42"));
*
* with 'query()' returning a reference to an object with a 'addparam()' member function which
* in turn returns a reference to that object. There are actually four ways you can create a
* SQLrequest..all have their disadvantages and advantages. In the real implementations the
* 'query()' function is replaced by the constructor of another class 'SQLquery' which holds
* the query string and a ParamL (std::deque<std::string>) of query parameters.
* This is essentially the same as the above example except 'addparam()' is replaced by operator,(). The full syntax for this method is:
*
* SQLrequest foo = SQLrequest(this, target, "databaseid", (SQLquery("SELECT.. ?"), parameter, parameter));
*/
class SQLquery : public classbase
{
public:
/** The query 'format string'
*/
std::string q;
/** The query parameter list
* There should be one parameter for every ? character
* within the format string shown above.
*/
ParamL p;
/** Initialize an SQLquery with a given format string only
*/
SQLquery(const std::string &query)
: q(query)
{
}
/** Initialize an SQLquery with a format string and parameters.
* If you provide parameters, you must initialize the list yourself
* if you choose to do it via this method, using std::deque::push_back().
*/
SQLquery(const std::string &query, const ParamL ¶ms)
: q(query), p(params)
{
}
/** An overloaded operator for pushing parameters onto the parameter list
*/
template<typename T> SQLquery& operator,(const T &foo)
{
p.push_back(ConvToStr(foo));
return *this;
}
/** An overloaded operator for pushing parameters onto the parameter list.
* This has higher precedence than 'operator,' and can save on parenthesis.
*/
template<typename T> SQLquery& operator%(const T &foo)
{
p.push_back(ConvToStr(foo));
return *this;
}
};
/** SQLrequest is sent to the SQL API to command it to run a query and return the result.
* You must instantiate this object with a valid SQLquery object and its parameters, then
* send it using its Send() method to the module providing the 'SQL' feature. To find this
* module, use Server::FindFeature().
*/
class SQLrequest : public Request
{
public:
/** The fully parsed and expanded query string
* This is initialized from the SQLquery parameter given in the constructor.
*/
SQLquery query;
/** The database ID to apply the request to
*/
std::string dbid;
/** True if this is a priority query.
* Priority queries may 'queue jump' in the request queue.
*/
bool pri;
/** The query ID, assigned by the SQL api.
* After your request is processed, this will
* be initialized for you by the API to a valid request ID,
* except in the case of an error.
*/
unsigned long id;
/** If an error occured, error.id will be any other value than NO_ERROR.
*/
SQLerror error;
/** Initialize an SQLrequest.
* For example:
*
* SQLrequest req = SQLreq(MyMod, SQLModule, dbid, "INSERT INTO ircd_log_actors VALUES('','?')", nick);
*
* @param s A pointer to the sending module, where the result should be routed
* @param d A pointer to the receiving module, identified as implementing the 'SQL' feature
* @param databaseid The database ID to perform the query on. This must match a valid
* database ID from the configuration of the SQL module.
* @param q A properly initialized SQLquery object.
*/
SQLrequest(Module* s, Module* d, const std::string &databaseid, const SQLquery &q)
: Request(s, d, SQLREQID), query(q), dbid(databaseid), pri(false), id(0)
{
}
/** Set the priority of a request.
*/
void Priority(bool p = true)
{
pri = p;
}
/** Set the source of a request. You should not need to use this method.
*/
void SetSource(Module* mod)
{
source = mod;
}
};
/**
* This class contains a field's data plus a way to determine if the field
* is NULL or not without having to mess around with NULL pointers.
*/
class SQLfield
{
public:
/**
* The data itself
*/
std::string d;
/**
* If the field was null
*/
bool null;
/** Initialize an SQLfield
*/
SQLfield(const std::string &data = "", bool n = false)
: d(data), null(n)
{
}
};
/** A list of items which make up a row of a result or table (tuple)
* This does not include field names.
*/
typedef std::vector<SQLfield> SQLfieldList;
/** A list of items which make up a row of a result or table (tuple)
* This also includes the field names.
*/
typedef std::map<std::string, SQLfield> SQLfieldMap;
/** SQLresult is a reply to a previous query.
* If you send a query to the SQL api, the response will arrive at your
* OnRequest method of your module at some later time, depending on the
* congestion of the SQL server and complexity of the query. The ID of
* this result will match the ID assigned to your original request.
* SQLresult contains its own internal cursor (row counter) which is
* incremented with each method call which retrieves a single row.
*/
class SQLresult : public Request
{
public:
/** The original query string passed initially to the SQL API
*/
std::string query;
/** The database ID the query was executed on
*/
std::string dbid;
/**
* The error (if any) which occured.
* If an error occured the value of error.id will be any
* other value than NO_ERROR.
*/
SQLerror error;
/**
* This will match query ID you were given when sending
* the request at an earlier time.
*/
unsigned long id;
/** Used by the SQL API to instantiate an SQLrequest
*/
SQLresult(Module* s, Module* d, unsigned long i)
: Request(s, d, SQLRESID), id(i)
{
}
/**
* Return the number of rows in the result
* Note that if you have perfomed an INSERT
* or UPDATE query or other query which will
* not return rows, this will return the
* number of affected rows, and SQLresult::Cols()
* will contain 0. In this case you SHOULD NEVER
* access any of the result set rows, as there arent any!
* @returns Number of rows in the result set.
*/
virtual int Rows() = 0;
/**
* Return the number of columns in the result.
* If you performed an UPDATE or INSERT which
* does not return a dataset, this value will
* be 0.
* @returns Number of columns in the result set.
*/
virtual int Cols() = 0;
/**
* Get a string name of the column by an index number
* @param column The id number of a column
* @returns The column name associated with the given ID
*/
virtual std::string ColName(int column) = 0;
/**
* Get an index number for a column from a string name.
* An exception of type SQLbadColName will be thrown if
* the name given is invalid.
* @param column The column name to get the ID of
* @returns The ID number of the column provided
*/
virtual int ColNum(const std::string &column) = 0;
/**
* Get a string value in a given row and column
* This does not effect the internal cursor.
* @returns The value stored at [row,column] in the table
*/
virtual SQLfield GetValue(int row, int column) = 0;
/**
* Return a list of values in a row, this should
* increment an internal counter so you can repeatedly
* call it until it returns an empty vector.
* This returns a reference to an internal object,
* the same object is used for all calls to this function
* and therefore the return value is only valid until
* you call this function again. It is also invalid if
* the SQLresult object is destroyed.
* The internal cursor (row counter) is incremented by one.
* @returns A reference to the current row's SQLfieldList
*/
virtual SQLfieldList& GetRow() = 0;
/**
* As above, but return a map indexed by key name.
* The internal cursor (row counter) is incremented by one.
* @returns A reference to the current row's SQLfieldMap
*/
virtual SQLfieldMap& GetRowMap() = 0;
/**
* Like GetRow(), but returns a pointer to a dynamically
* allocated object which must be explicitly freed. For
* portability reasons this must be freed with SQLresult::Free()
* The internal cursor (row counter) is incremented by one.
* @returns A newly-allocated SQLfieldList
*/
virtual SQLfieldList* GetRowPtr() = 0;
/**
* As above, but return a map indexed by key name
* The internal cursor (row counter) is incremented by one.
* @returns A newly-allocated SQLfieldMap
*/
virtual SQLfieldMap* GetRowMapPtr() = 0;
/**
* Overloaded function for freeing the lists and maps
* returned by GetRowPtr or GetRowMapPtr.
* @param fm The SQLfieldMap to free
*/
virtual void Free(SQLfieldMap* fm) = 0;
/**
* Overloaded function for freeing the lists and maps
* returned by GetRowPtr or GetRowMapPtr.
* @param fl The SQLfieldList to free
*/
virtual void Free(SQLfieldList* fl) = 0;
};
/** SQLHost represents a <database> config line and is useful
* for storing in a map and iterating on rehash to see which
* <database> tags was added/removed/unchanged.
*/
class SQLhost
{
public:
std::string id; /* Database handle id */
std::string host; /* Database server hostname */
std::string ip; /* resolved IP, needed for at least pgsql.so */
unsigned int port; /* Database server port */
std::string name; /* Database name */
std::string user; /* Database username */
std::string pass; /* Database password */
bool ssl; /* If we should require SSL */
SQLhost()
{
}
SQLhost(const std::string& i, const std::string& h, unsigned int p, const std::string& n, const std::string& u, const std::string& pa, bool s)
: id(i), host(h), port(p), name(n), user(u), pass(pa), ssl(s)
{
}
/** Overload this to return a correct Data source Name (DSN) for
* the current SQL module.
*/
std::string GetDSN();
};
/** Overload operator== for two SQLhost objects for easy comparison.
*/
bool operator== (const SQLhost& l, const SQLhost& r)
{
return (l.id == r.id && l.host == r.host && l.port == r.port && l.name == r.name && l.user == l.user && l.pass == r.pass && l.ssl == r.ssl);
}
/** QueryQueue, a queue of queries waiting to be executed.
* This maintains two queues internally, one for 'priority'
* queries and one for less important ones. Each queue has
* new queries appended to it and ones to execute are popped
* off the front. This keeps them flowing round nicely and no
* query should ever get 'stuck' for too long. If there are
* queries in the priority queue they will be executed first,
* 'unimportant' queries will only be executed when the
* priority queue is empty.
*
* We store lists of SQLrequest's here, by value as we want to avoid storing
* any data allocated inside the client module (in case that module is unloaded
* while the query is in progress).
*
* Because we want to work on the current SQLrequest in-situ, we need a way
* of accessing the request we are currently processing, QueryQueue::front(),
* but that call needs to always return the same request until that request
* is removed from the queue, this is what the 'which' variable is. New queries are
* always added to the back of one of the two queues, but if when front()
* is first called then the priority queue is empty then front() will return
* a query from the normal queue, but if a query is then added to the priority
* queue then front() must continue to return the front of the *normal* queue
* until pop() is called.
*/
class QueryQueue : public classbase
{
private:
typedef std::deque<SQLrequest> ReqDeque;
ReqDeque priority; /* The priority queue */
ReqDeque normal; /* The 'normal' queue */
enum { PRI, NOR, NON } which; /* Which queue the currently active element is at the front of */
public:
QueryQueue()
: which(NON)
{
}
void push(const SQLrequest &q)
{
if(q.pri)
priority.push_back(q);
else
normal.push_back(q);
}
void pop()
{
if((which == PRI) && priority.size())
{
priority.pop_front();
}
else if((which == NOR) && normal.size())
{
normal.pop_front();
}
/* Reset this */
which = NON;
/* Silently do nothing if there was no element to pop() */
}
SQLrequest& front()
{
switch(which)
{
case PRI:
return priority.front();
case NOR:
return normal.front();
default:
if(priority.size())
{
which = PRI;
return priority.front();
}
if(normal.size())
{
which = NOR;
return normal.front();
}
/* This will probably result in a segfault,
* but the caller should have checked totalsize()
* first so..meh - moron :p
*/
return priority.front();
}
}
std::pair<int, int> size()
{
return std::make_pair(priority.size(), normal.size());
}
int totalsize()
{
return priority.size() + normal.size();
}
void PurgeModule(Module* mod)
{
DoPurgeModule(mod, priority);
DoPurgeModule(mod, normal);
}
private:
void DoPurgeModule(Module* mod, ReqDeque& q)
{
for(ReqDeque::iterator iter = q.begin(); iter != q.end(); iter++)
{
if(iter->GetSource() == mod)
{
if(iter->id == front().id)
{
/* It's the currently active query.. :x */
iter->SetSource(NULL);
}
else
{
/* It hasn't been executed yet..just remove it */
iter = q.erase(iter);
}
}
}
}
};
#endif
\ No newline at end of file +/* +------------------------------------+ + * | 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. + * + * --------------------------------------------------- + */ + +#ifndef INSPIRCD_SQLAPI_2 +#define INSPIRCD_SQLAPI_2 + +#include <string> +#include <deque> +#include <map> +#include "modules.h" + +/** SQLreq define. + * This is the voodoo magic which lets us pass multiple + * parameters to the SQLrequest constructor... voodoo... + */ +#define SQLreq(a, b, c, d, e...) SQLrequest(a, b, c, (SQLquery(d), ##e)) + +/** Identifiers used to identify Request types + */ +#define SQLREQID "SQLv2 Request" +#define SQLRESID "SQLv2 Result" +#define SQLSUCCESS "You shouldn't be reading this (success)" + +/** Defines the error types which SQLerror may be set to + */ +enum SQLerrorNum { NO_ERROR, BAD_DBID, BAD_CONN, QSEND_FAIL, QREPLY_FAIL }; + +/** A list of format parameters for an SQLquery object. + */ +typedef std::deque<std::string> ParamL; + +/** The base class of SQL exceptions + */ +class SQLexception : public ModuleException +{ + public: + SQLexception(const std::string &reason) : ModuleException(reason) + { + } + + SQLexception() : ModuleException("SQLv2: Undefined exception") + { + } +}; + +/** An exception thrown when a bad column or row name or id is requested + */ +class SQLbadColName : public SQLexception +{ +public: + SQLbadColName() : SQLexception("SQLv2: Bad column name") + { + } +}; + +/** SQLerror holds the error state of any SQLrequest or SQLresult. + * The error string varies from database software to database software + * and should be used to display informational error messages to users. + */ +class SQLerror : public classbase +{ + /** The error id + */ + SQLerrorNum id; + /** The error string + */ + std::string str; +public: + /** Initialize an SQLerror + * @param i The error ID to set + * @param s The (optional) error string to set + */ + SQLerror(SQLerrorNum i = NO_ERROR, const std::string &s = "") + : id(i), str(s) + { + } + + /** Return the ID of the error + */ + SQLerrorNum Id() + { + return id; + } + + /** Set the ID of an error + * @param i The new error ID to set + * @return the ID which was set + */ + SQLerrorNum Id(SQLerrorNum i) + { + id = i; + return id; + } + + /** Set the error string for an error + * @param s The new error string to set + */ + void Str(const std::string &s) + { + str = s; + } + + /** Return the error string for an error + */ + const char* Str() + { + if(str.length()) + return str.c_str(); + + switch(id) + { + case NO_ERROR: + return "No error"; + case BAD_DBID: + return "Invalid database ID"; + case BAD_CONN: + return "Invalid connection"; + case QSEND_FAIL: + return "Sending query failed"; + case QREPLY_FAIL: + return "Getting query result failed"; + default: + return "Unknown error"; + } + } +}; + +/** SQLquery provides a way to represent a query string, and its parameters in a type-safe way. + * C++ has no native type-safe way of having a variable number of arguments to a function, + * the workaround for this isn't easy to describe simply, but in a nutshell what's really + * happening when - from the above example - you do this: + * + * SQLrequest foo = SQLreq(this, target, "databaseid", "SELECT (foo, bar) FROM rawr WHERE foo = '?' AND bar = ?", "Hello", "42"); + * + * what's actually happening is functionally this: + * + * SQLrequest foo = SQLreq(this, target, "databaseid", query("SELECT (foo, bar) FROM rawr WHERE foo = '?' AND bar = ?").addparam("Hello").addparam("42")); + * + * with 'query()' returning a reference to an object with a 'addparam()' member function which + * in turn returns a reference to that object. There are actually four ways you can create a + * SQLrequest..all have their disadvantages and advantages. In the real implementations the + * 'query()' function is replaced by the constructor of another class 'SQLquery' which holds + * the query string and a ParamL (std::deque<std::string>) of query parameters. + * This is essentially the same as the above example except 'addparam()' is replaced by operator,(). The full syntax for this method is: + * + * SQLrequest foo = SQLrequest(this, target, "databaseid", (SQLquery("SELECT.. ?"), parameter, parameter)); + */ +class SQLquery : public classbase +{ +public: + /** The query 'format string' + */ + std::string q; + /** The query parameter list + * There should be one parameter for every ? character + * within the format string shown above. + */ + ParamL p; + + /** Initialize an SQLquery with a given format string only + */ + SQLquery(const std::string &query) + : q(query) + { + } + + /** Initialize an SQLquery with a format string and parameters. + * If you provide parameters, you must initialize the list yourself + * if you choose to do it via this method, using std::deque::push_back(). + */ + SQLquery(const std::string &query, const ParamL ¶ms) + : q(query), p(params) + { + } + + /** An overloaded operator for pushing parameters onto the parameter list + */ + template<typename T> SQLquery& operator,(const T &foo) + { + p.push_back(ConvToStr(foo)); + return *this; + } + + /** An overloaded operator for pushing parameters onto the parameter list. + * This has higher precedence than 'operator,' and can save on parenthesis. + */ + template<typename T> SQLquery& operator%(const T &foo) + { + p.push_back(ConvToStr(foo)); + return *this; + } +}; + +/** SQLrequest is sent to the SQL API to command it to run a query and return the result. + * You must instantiate this object with a valid SQLquery object and its parameters, then + * send it using its Send() method to the module providing the 'SQL' feature. To find this + * module, use Server::FindFeature(). + */ +class SQLrequest : public Request +{ +public: + /** The fully parsed and expanded query string + * This is initialized from the SQLquery parameter given in the constructor. + */ + SQLquery query; + /** The database ID to apply the request to + */ + std::string dbid; + /** True if this is a priority query. + * Priority queries may 'queue jump' in the request queue. + */ + bool pri; + /** The query ID, assigned by the SQL api. + * After your request is processed, this will + * be initialized for you by the API to a valid request ID, + * except in the case of an error. + */ + unsigned long id; + /** If an error occured, error.id will be any other value than NO_ERROR. + */ + SQLerror error; + + /** Initialize an SQLrequest. + * For example: + * + * SQLrequest req = SQLreq(MyMod, SQLModule, dbid, "INSERT INTO ircd_log_actors VALUES('','?')", nick); + * + * @param s A pointer to the sending module, where the result should be routed + * @param d A pointer to the receiving module, identified as implementing the 'SQL' feature + * @param databaseid The database ID to perform the query on. This must match a valid + * database ID from the configuration of the SQL module. + * @param q A properly initialized SQLquery object. + */ + SQLrequest(Module* s, Module* d, const std::string &databaseid, const SQLquery &q) + : Request(s, d, SQLREQID), query(q), dbid(databaseid), pri(false), id(0) + { + } + + /** Set the priority of a request. + */ + void Priority(bool p = true) + { + pri = p; + } + + /** Set the source of a request. You should not need to use this method. + */ + void SetSource(Module* mod) + { + source = mod; + } +}; + +/** + * This class contains a field's data plus a way to determine if the field + * is NULL or not without having to mess around with NULL pointers. + */ +class SQLfield +{ +public: + /** + * The data itself + */ + std::string d; + + /** + * If the field was null + */ + bool null; + + /** Initialize an SQLfield + */ + SQLfield(const std::string &data = "", bool n = false) + : d(data), null(n) + { + + } +}; + +/** A list of items which make up a row of a result or table (tuple) + * This does not include field names. + */ +typedef std::vector<SQLfield> SQLfieldList; +/** A list of items which make up a row of a result or table (tuple) + * This also includes the field names. + */ +typedef std::map<std::string, SQLfield> SQLfieldMap; + +/** SQLresult is a reply to a previous query. + * If you send a query to the SQL api, the response will arrive at your + * OnRequest method of your module at some later time, depending on the + * congestion of the SQL server and complexity of the query. The ID of + * this result will match the ID assigned to your original request. + * SQLresult contains its own internal cursor (row counter) which is + * incremented with each method call which retrieves a single row. + */ +class SQLresult : public Request +{ +public: + /** The original query string passed initially to the SQL API + */ + std::string query; + /** The database ID the query was executed on + */ + std::string dbid; + /** + * The error (if any) which occured. + * If an error occured the value of error.id will be any + * other value than NO_ERROR. + */ + SQLerror error; + /** + * This will match query ID you were given when sending + * the request at an earlier time. + */ + unsigned long id; + + /** Used by the SQL API to instantiate an SQLrequest + */ + SQLresult(Module* s, Module* d, unsigned long i) + : Request(s, d, SQLRESID), id(i) + { + } + + /** + * Return the number of rows in the result + * Note that if you have perfomed an INSERT + * or UPDATE query or other query which will + * not return rows, this will return the + * number of affected rows, and SQLresult::Cols() + * will contain 0. In this case you SHOULD NEVER + * access any of the result set rows, as there arent any! + * @returns Number of rows in the result set. + */ + virtual int Rows() = 0; + + /** + * Return the number of columns in the result. + * If you performed an UPDATE or INSERT which + * does not return a dataset, this value will + * be 0. + * @returns Number of columns in the result set. + */ + virtual int Cols() = 0; + + /** + * Get a string name of the column by an index number + * @param column The id number of a column + * @returns The column name associated with the given ID + */ + virtual std::string ColName(int column) = 0; + + /** + * Get an index number for a column from a string name. + * An exception of type SQLbadColName will be thrown if + * the name given is invalid. + * @param column The column name to get the ID of + * @returns The ID number of the column provided + */ + virtual int ColNum(const std::string &column) = 0; + + /** + * Get a string value in a given row and column + * This does not effect the internal cursor. + * @returns The value stored at [row,column] in the table + */ + virtual SQLfield GetValue(int row, int column) = 0; + + /** + * Return a list of values in a row, this should + * increment an internal counter so you can repeatedly + * call it until it returns an empty vector. + * This returns a reference to an internal object, + * the same object is used for all calls to this function + * and therefore the return value is only valid until + * you call this function again. It is also invalid if + * the SQLresult object is destroyed. + * The internal cursor (row counter) is incremented by one. + * @returns A reference to the current row's SQLfieldList + */ + virtual SQLfieldList& GetRow() = 0; + + /** + * As above, but return a map indexed by key name. + * The internal cursor (row counter) is incremented by one. + * @returns A reference to the current row's SQLfieldMap + */ + virtual SQLfieldMap& GetRowMap() = 0; + + /** + * Like GetRow(), but returns a pointer to a dynamically + * allocated object which must be explicitly freed. For + * portability reasons this must be freed with SQLresult::Free() + * The internal cursor (row counter) is incremented by one. + * @returns A newly-allocated SQLfieldList + */ + virtual SQLfieldList* GetRowPtr() = 0; + + /** + * As above, but return a map indexed by key name + * The internal cursor (row counter) is incremented by one. + * @returns A newly-allocated SQLfieldMap + */ + virtual SQLfieldMap* GetRowMapPtr() = 0; + + /** + * Overloaded function for freeing the lists and maps + * returned by GetRowPtr or GetRowMapPtr. + * @param fm The SQLfieldMap to free + */ + virtual void Free(SQLfieldMap* fm) = 0; + + /** + * Overloaded function for freeing the lists and maps + * returned by GetRowPtr or GetRowMapPtr. + * @param fl The SQLfieldList to free + */ + virtual void Free(SQLfieldList* fl) = 0; +}; + + +/** SQLHost represents a <database> config line and is useful + * for storing in a map and iterating on rehash to see which + * <database> tags was added/removed/unchanged. + */ +class SQLhost +{ + public: + std::string id; /* Database handle id */ + std::string host; /* Database server hostname */ + std::string ip; /* resolved IP, needed for at least pgsql.so */ + unsigned int port; /* Database server port */ + std::string name; /* Database name */ + std::string user; /* Database username */ + std::string pass; /* Database password */ + bool ssl; /* If we should require SSL */ + + SQLhost() + { + } + + SQLhost(const std::string& i, const std::string& h, unsigned int p, const std::string& n, const std::string& u, const std::string& pa, bool s) + : id(i), host(h), port(p), name(n), user(u), pass(pa), ssl(s) + { + } + + /** Overload this to return a correct Data source Name (DSN) for + * the current SQL module. + */ + std::string GetDSN(); +}; + +/** Overload operator== for two SQLhost objects for easy comparison. + */ +bool operator== (const SQLhost& l, const SQLhost& r) +{ + return (l.id == r.id && l.host == r.host && l.port == r.port && l.name == r.name && l.user == l.user && l.pass == r.pass && l.ssl == r.ssl); +} + + +/** QueryQueue, a queue of queries waiting to be executed. + * This maintains two queues internally, one for 'priority' + * queries and one for less important ones. Each queue has + * new queries appended to it and ones to execute are popped + * off the front. This keeps them flowing round nicely and no + * query should ever get 'stuck' for too long. If there are + * queries in the priority queue they will be executed first, + * 'unimportant' queries will only be executed when the + * priority queue is empty. + * + * We store lists of SQLrequest's here, by value as we want to avoid storing + * any data allocated inside the client module (in case that module is unloaded + * while the query is in progress). + * + * Because we want to work on the current SQLrequest in-situ, we need a way + * of accessing the request we are currently processing, QueryQueue::front(), + * but that call needs to always return the same request until that request + * is removed from the queue, this is what the 'which' variable is. New queries are + * always added to the back of one of the two queues, but if when front() + * is first called then the priority queue is empty then front() will return + * a query from the normal queue, but if a query is then added to the priority + * queue then front() must continue to return the front of the *normal* queue + * until pop() is called. + */ + +class QueryQueue : public classbase +{ +private: + typedef std::deque<SQLrequest> ReqDeque; + + ReqDeque priority; /* The priority queue */ + ReqDeque normal; /* The 'normal' queue */ + enum { PRI, NOR, NON } which; /* Which queue the currently active element is at the front of */ + +public: + QueryQueue() + : which(NON) + { + } + + void push(const SQLrequest &q) + { + if(q.pri) + priority.push_back(q); + else + normal.push_back(q); + } + + void pop() + { + if((which == PRI) && priority.size()) + { + priority.pop_front(); + } + else if((which == NOR) && normal.size()) + { + normal.pop_front(); + } + + /* Reset this */ + which = NON; + + /* Silently do nothing if there was no element to pop() */ + } + + SQLrequest& front() + { + switch(which) + { + case PRI: + return priority.front(); + case NOR: + return normal.front(); + default: + if(priority.size()) + { + which = PRI; + return priority.front(); + } + + if(normal.size()) + { + which = NOR; + return normal.front(); + } + + /* This will probably result in a segfault, + * but the caller should have checked totalsize() + * first so..meh - moron :p + */ + + return priority.front(); + } + } + + std::pair<int, int> size() + { + return std::make_pair(priority.size(), normal.size()); + } + + int totalsize() + { + return priority.size() + normal.size(); + } + + void PurgeModule(Module* mod) + { + DoPurgeModule(mod, priority); + DoPurgeModule(mod, normal); + } + +private: + void DoPurgeModule(Module* mod, ReqDeque& q) + { + for(ReqDeque::iterator iter = q.begin(); iter != q.end(); iter++) + { + if(iter->GetSource() == mod) + { + if(iter->id == front().id) + { + /* It's the currently active query.. :x */ + iter->SetSource(NULL); + } + else + { + /* It hasn't been executed yet..just remove it */ + iter = q.erase(iter); + } + } + } + } +}; + + +#endif diff --git a/src/modules/extra/m_ssl_gnutls.cpp b/src/modules/extra/m_ssl_gnutls.cpp index 037d2cf72..fd8b12d32 100644 --- a/src/modules/extra/m_ssl_gnutls.cpp +++ b/src/modules/extra/m_ssl_gnutls.cpp @@ -1 +1,843 @@ -/* +------------------------------------+
* | 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 <gnutls/gnutls.h>
#include <gnutls/x509.h>
#include "inspircd_config.h"
#include "configreader.h"
#include "users.h"
#include "channels.h"
#include "modules.h"
#include "socket.h"
#include "hashcomp.h"
#include "transport.h"
#ifdef WINDOWS
#pragma comment(lib, "libgnutls-13.lib")
#undef MAX_DESCRIPTORS
#define MAX_DESCRIPTORS 10000
#endif
/* $ModDesc: Provides SSL support for clients */
/* $CompileFlags: exec("libgnutls-config --cflags") */
/* $LinkerFlags: rpath("libgnutls-config --libs") exec("libgnutls-config --libs") */
/* $ModDep: transport.h */
enum issl_status { ISSL_NONE, ISSL_HANDSHAKING_READ, ISSL_HANDSHAKING_WRITE, ISSL_HANDSHAKEN, ISSL_CLOSING, ISSL_CLOSED };
bool isin(int port, const std::vector<int> &portlist)
{
for(unsigned int i = 0; i < portlist.size(); i++)
if(portlist[i] == port)
return true;
return false;
}
/** Represents an SSL user's extra data
*/
class issl_session : public classbase
{
public:
gnutls_session_t sess;
issl_status status;
std::string outbuf;
int inbufoffset;
char* inbuf;
int fd;
};
class ModuleSSLGnuTLS : public Module
{
ConfigReader* Conf;
char* dummy;
std::vector<int> listenports;
int inbufsize;
issl_session sessions[MAX_DESCRIPTORS];
gnutls_certificate_credentials x509_cred;
gnutls_dh_params dh_params;
std::string keyfile;
std::string certfile;
std::string cafile;
std::string crlfile;
std::string sslports;
int dh_bits;
int clientactive;
public:
ModuleSSLGnuTLS(InspIRCd* Me)
: Module(Me)
{
ServerInstance->PublishInterface("InspSocketHook", this);
// Not rehashable...because I cba to reduce all the sizes of existing buffers.
inbufsize = ServerInstance->Config->NetBufferSize;
gnutls_global_init(); // This must be called once in the program
if(gnutls_certificate_allocate_credentials(&x509_cred) != 0)
ServerInstance->Log(DEFAULT, "m_ssl_gnutls.so: Failed to allocate certificate credentials");
// Guessing return meaning
if(gnutls_dh_params_init(&dh_params) < 0)
ServerInstance->Log(DEFAULT, "m_ssl_gnutls.so: Failed to initialise DH parameters");
// Needs the flag as it ignores a plain /rehash
OnRehash(NULL,"ssl");
// Void return, guess we assume success
gnutls_certificate_set_dh_params(x509_cred, dh_params);
}
virtual void OnRehash(userrec* user, const std::string ¶m)
{
if(param != "ssl")
return;
Conf = new ConfigReader(ServerInstance);
for(unsigned int i = 0; i < listenports.size(); i++)
{
ServerInstance->Config->DelIOHook(listenports[i]);
}
listenports.clear();
clientactive = 0;
sslports.clear();
for(int i = 0; i < Conf->Enumerate("bind"); i++)
{
// For each <bind> tag
std::string x = Conf->ReadValue("bind", "type", i);
if(((x.empty()) || (x == "clients")) && (Conf->ReadValue("bind", "ssl", i) == "gnutls"))
{
// Get the port we're meant to be listening on with SSL
std::string port = Conf->ReadValue("bind", "port", i);
irc::portparser portrange(port, false);
long portno = -1;
while ((portno = portrange.GetToken()))
{
clientactive++;
try
{
if (ServerInstance->Config->AddIOHook(portno, this))
{
listenports.push_back(portno);
for (size_t i = 0; i < ServerInstance->Config->ports.size(); i++)
if (ServerInstance->Config->ports[i]->GetPort() == portno)
ServerInstance->Config->ports[i]->SetDescription("ssl");
ServerInstance->Log(DEFAULT, "m_ssl_gnutls.so: Enabling SSL for port %d", portno);
sslports.append("*:").append(ConvToStr(portno)).append(";");
}
else
{
ServerInstance->Log(DEFAULT, "m_ssl_gnutls.so: FAILED to enable SSL on port %d, maybe you have another ssl or similar module loaded?", portno);
}
}
catch (ModuleException &e)
{
ServerInstance->Log(DEFAULT, "m_ssl_gnutls.so: FAILED to enable SSL on port %d: %s. Maybe it's already hooked by the same port on a different IP, or you have an other SSL or similar module loaded?", portno, e.GetReason());
}
}
}
}
std::string confdir(ServerInstance->ConfigFileName);
// +1 so we the path ends with a /
confdir = confdir.substr(0, confdir.find_last_of('/') + 1);
cafile = Conf->ReadValue("gnutls", "cafile", 0);
crlfile = Conf->ReadValue("gnutls", "crlfile", 0);
certfile = Conf->ReadValue("gnutls", "certfile", 0);
keyfile = Conf->ReadValue("gnutls", "keyfile", 0);
dh_bits = Conf->ReadInteger("gnutls", "dhbits", 0, false);
// Set all the default values needed.
if (cafile.empty())
cafile = "ca.pem";
if (crlfile.empty())
crlfile = "crl.pem";
if (certfile.empty())
certfile = "cert.pem";
if (keyfile.empty())
keyfile = "key.pem";
if((dh_bits != 768) && (dh_bits != 1024) && (dh_bits != 2048) && (dh_bits != 3072) && (dh_bits != 4096))
dh_bits = 1024;
// Prepend relative paths with the path to the config directory.
if(cafile[0] != '/')
cafile = confdir + cafile;
if(crlfile[0] != '/')
crlfile = confdir + crlfile;
if(certfile[0] != '/')
certfile = confdir + certfile;
if(keyfile[0] != '/')
keyfile = confdir + keyfile;
int ret;
if((ret =gnutls_certificate_set_x509_trust_file(x509_cred, cafile.c_str(), GNUTLS_X509_FMT_PEM)) < 0)
ServerInstance->Log(DEFAULT, "m_ssl_gnutls.so: Failed to set X.509 trust file '%s': %s", cafile.c_str(), gnutls_strerror(ret));
if((ret = gnutls_certificate_set_x509_crl_file (x509_cred, crlfile.c_str(), GNUTLS_X509_FMT_PEM)) < 0)
ServerInstance->Log(DEFAULT, "m_ssl_gnutls.so: Failed to set X.509 CRL file '%s': %s", crlfile.c_str(), gnutls_strerror(ret));
if((ret = gnutls_certificate_set_x509_key_file (x509_cred, certfile.c_str(), keyfile.c_str(), GNUTLS_X509_FMT_PEM)) < 0)
{
// If this fails, no SSL port will work. At all. So, do the smart thing - throw a ModuleException
throw ModuleException("Unable to load GnuTLS server certificate: " + std::string(gnutls_strerror(ret)));
}
// This may be on a large (once a day or week) timer eventually.
GenerateDHParams();
DELETE(Conf);
}
void GenerateDHParams()
{
// Generate Diffie Hellman parameters - for use with DHE
// kx algorithms. These should be discarded and regenerated
// once a day, once a week or once a month. Depending on the
// security requirements.
int ret;
if((ret = gnutls_dh_params_generate2(dh_params, dh_bits)) < 0)
ServerInstance->Log(DEFAULT, "m_ssl_gnutls.so: Failed to generate DH parameters (%d bits): %s", dh_bits, gnutls_strerror(ret));
}
virtual ~ModuleSSLGnuTLS()
{
gnutls_dh_params_deinit(dh_params);
gnutls_certificate_free_credentials(x509_cred);
gnutls_global_deinit();
}
virtual void OnCleanup(int target_type, void* item)
{
if(target_type == TYPE_USER)
{
userrec* user = (userrec*)item;
if(user->GetExt("ssl", dummy) && isin(user->GetPort(), listenports))
{
// User is using SSL, they're a local user, and they're using one of *our* SSL ports.
// Potentially there could be multiple SSL modules loaded at once on different ports.
ServerInstance->GlobalCulls.AddItem(user, "SSL module unloading");
}
if (user->GetExt("ssl_cert", dummy) && isin(user->GetPort(), listenports))
{
ssl_cert* tofree;
user->GetExt("ssl_cert", tofree);
delete tofree;
user->Shrink("ssl_cert");
}
}
}
virtual void OnUnloadModule(Module* mod, const std::string &name)
{
if(mod == this)
{
for(unsigned int i = 0; i < listenports.size(); i++)
{
ServerInstance->Config->DelIOHook(listenports[i]);
for (size_t j = 0; j < ServerInstance->Config->ports.size(); j++)
if (ServerInstance->Config->ports[j]->GetPort() == listenports[i])
ServerInstance->Config->ports[j]->SetDescription("plaintext");
}
}
}
virtual Version GetVersion()
{
return Version(1, 1, 0, 0, VF_VENDOR, API_VERSION);
}
void Implements(char* List)
{
List[I_On005Numeric] = List[I_OnRawSocketConnect] = List[I_OnRawSocketAccept] = List[I_OnRawSocketClose] = List[I_OnRawSocketRead] = List[I_OnRawSocketWrite] = List[I_OnCleanup] = 1;
List[I_OnRequest] = List[I_OnSyncUserMetaData] = List[I_OnDecodeMetaData] = List[I_OnUnloadModule] = List[I_OnRehash] = List[I_OnWhois] = List[I_OnPostConnect] = 1;
}
virtual void On005Numeric(std::string &output)
{
output.append(" SSL=" + sslports);
}
virtual char* OnRequest(Request* request)
{
ISHRequest* ISR = (ISHRequest*)request;
if (strcmp("IS_NAME", request->GetId()) == 0)
{
return "gnutls";
}
else if (strcmp("IS_HOOK", request->GetId()) == 0)
{
char* ret = "OK";
try
{
ret = ServerInstance->Config->AddIOHook((Module*)this, (InspSocket*)ISR->Sock) ? (char*)"OK" : NULL;
}
catch (ModuleException &e)
{
return NULL;
}
return ret;
}
else if (strcmp("IS_UNHOOK", request->GetId()) == 0)
{
return ServerInstance->Config->DelIOHook((InspSocket*)ISR->Sock) ? (char*)"OK" : NULL;
}
else if (strcmp("IS_HSDONE", request->GetId()) == 0)
{
if (ISR->Sock->GetFd() < 0)
return (char*)"OK";
issl_session* session = &sessions[ISR->Sock->GetFd()];
return (session->status == ISSL_HANDSHAKING_READ || session->status == ISSL_HANDSHAKING_WRITE) ? NULL : (char*)"OK";
}
else if (strcmp("IS_ATTACH", request->GetId()) == 0)
{
if (ISR->Sock->GetFd() > -1)
{
issl_session* session = &sessions[ISR->Sock->GetFd()];
if (session->sess)
{
if ((Extensible*)ServerInstance->FindDescriptor(ISR->Sock->GetFd()) == (Extensible*)(ISR->Sock))
{
VerifyCertificate(session, (InspSocket*)ISR->Sock);
return "OK";
}
}
}
}
return NULL;
}
virtual void OnRawSocketAccept(int fd, const std::string &ip, int localport)
{
issl_session* session = &sessions[fd];
session->fd = fd;
session->inbuf = new char[inbufsize];
session->inbufoffset = 0;
gnutls_init(&session->sess, GNUTLS_SERVER);
gnutls_set_default_priority(session->sess); // Avoid calling all the priority functions, defaults are adequate.
gnutls_credentials_set(session->sess, GNUTLS_CRD_CERTIFICATE, x509_cred);
gnutls_dh_set_prime_bits(session->sess, dh_bits);
/* This is an experimental change to avoid a warning on 64bit systems about casting between integer and pointer of different sizes
* This needs testing, but it's easy enough to rollback if need be
* Old: gnutls_transport_set_ptr(session->sess, (gnutls_transport_ptr_t) fd); // Give gnutls the fd for the socket.
* New: gnutls_transport_set_ptr(session->sess, &fd); // Give gnutls the fd for the socket.
*
* With testing this seems to...not work :/
*/
gnutls_transport_set_ptr(session->sess, (gnutls_transport_ptr_t) fd); // Give gnutls the fd for the socket.
gnutls_certificate_server_set_request(session->sess, GNUTLS_CERT_REQUEST); // Request client certificate if any.
Handshake(session);
}
virtual void OnRawSocketConnect(int fd)
{
issl_session* session = &sessions[fd];
session->fd = fd;
session->inbuf = new char[inbufsize];
session->inbufoffset = 0;
gnutls_init(&session->sess, GNUTLS_CLIENT);
gnutls_set_default_priority(session->sess); // Avoid calling all the priority functions, defaults are adequate.
gnutls_credentials_set(session->sess, GNUTLS_CRD_CERTIFICATE, x509_cred);
gnutls_dh_set_prime_bits(session->sess, dh_bits);
gnutls_transport_set_ptr(session->sess, (gnutls_transport_ptr_t) fd); // Give gnutls the fd for the socket.
Handshake(session);
}
virtual void OnRawSocketClose(int fd)
{
CloseSession(&sessions[fd]);
EventHandler* user = ServerInstance->SE->GetRef(fd);
if ((user) && (user->GetExt("ssl_cert", dummy)))
{
ssl_cert* tofree;
user->GetExt("ssl_cert", tofree);
delete tofree;
user->Shrink("ssl_cert");
}
}
virtual int OnRawSocketRead(int fd, char* buffer, unsigned int count, int &readresult)
{
issl_session* session = &sessions[fd];
if (!session->sess)
{
readresult = 0;
CloseSession(session);
return 1;
}
if (session->status == ISSL_HANDSHAKING_READ)
{
// The handshake isn't finished, try to finish it.
if(!Handshake(session))
{
// Couldn't resume handshake.
return -1;
}
}
else if (session->status == ISSL_HANDSHAKING_WRITE)
{
errno = EAGAIN;
return -1;
}
// If we resumed the handshake then session->status will be ISSL_HANDSHAKEN.
if (session->status == ISSL_HANDSHAKEN)
{
// Is this right? Not sure if the unencrypted data is garaunteed to be the same length.
// Read into the inbuffer, offset from the beginning by the amount of data we have that insp hasn't taken yet.
int ret = gnutls_record_recv(session->sess, session->inbuf + session->inbufoffset, inbufsize - session->inbufoffset);
if (ret == 0)
{
// Client closed connection.
readresult = 0;
CloseSession(session);
return 1;
}
else if (ret < 0)
{
if (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED)
{
errno = EAGAIN;
return -1;
}
else
{
readresult = 0;
CloseSession(session);
}
}
else
{
// Read successfully 'ret' bytes into inbuf + inbufoffset
// There are 'ret' + 'inbufoffset' bytes of data in 'inbuf'
// 'buffer' is 'count' long
unsigned int length = ret + session->inbufoffset;
if(count <= length)
{
memcpy(buffer, session->inbuf, count);
// Move the stuff left in inbuf to the beginning of it
memcpy(session->inbuf, session->inbuf + count, (length - count));
// Now we need to set session->inbufoffset to the amount of data still waiting to be handed to insp.
session->inbufoffset = length - count;
// Insp uses readresult as the count of how much data there is in buffer, so:
readresult = count;
}
else
{
// There's not as much in the inbuf as there is space in the buffer, so just copy the whole thing.
memcpy(buffer, session->inbuf, length);
// Zero the offset, as there's nothing there..
session->inbufoffset = 0;
// As above
readresult = length;
}
}
}
else if(session->status == ISSL_CLOSING)
readresult = 0;
return 1;
}
virtual int OnRawSocketWrite(int fd, const char* buffer, int count)
{
if (!count)
return 0;
issl_session* session = &sessions[fd];
const char* sendbuffer = buffer;
if (!session->sess)
{
ServerInstance->Log(DEBUG,"No session");
CloseSession(session);
return 1;
}
session->outbuf.append(sendbuffer, count);
sendbuffer = session->outbuf.c_str();
count = session->outbuf.size();
if (session->status == ISSL_HANDSHAKING_WRITE)
{
// The handshake isn't finished, try to finish it.
ServerInstance->Log(DEBUG,"Finishing handshake");
Handshake(session);
errno = EAGAIN;
return -1;
}
int ret = 0;
if (session->status == ISSL_HANDSHAKEN)
{
ServerInstance->Log(DEBUG,"Send record");
ret = gnutls_record_send(session->sess, sendbuffer, count);
ServerInstance->Log(DEBUG,"Return: %d", ret);
if (ret == 0)
{
CloseSession(session);
}
else if (ret < 0)
{
if(ret != GNUTLS_E_AGAIN && ret != GNUTLS_E_INTERRUPTED)
{
ServerInstance->Log(DEBUG,"Not egain or interrupt, close session");
CloseSession(session);
}
else
{
ServerInstance->Log(DEBUG,"Again please");
errno = EAGAIN;
return -1;
}
}
else
{
ServerInstance->Log(DEBUG,"Trim buffer");
session->outbuf = session->outbuf.substr(ret);
}
}
/* Who's smart idea was it to return 1 when we havent written anything?
* This fucks the buffer up in InspSocket :p
*/
return ret < 1 ? 0 : ret;
}
// :kenny.chatspike.net 320 Om Epy|AFK :is a Secure Connection
virtual void OnWhois(userrec* source, userrec* dest)
{
if (!clientactive)
return;
// Bugfix, only send this numeric for *our* SSL users
if(dest->GetExt("ssl", dummy) || (IS_LOCAL(dest) && isin(dest->GetPort(), listenports)))
{
ServerInstance->SendWhoisLine(source, dest, 320, "%s %s :is using a secure connection", source->nick, dest->nick);
}
}
virtual void OnSyncUserMetaData(userrec* user, Module* proto, void* opaque, const std::string &extname, bool displayable)
{
// check if the linking module wants to know about OUR metadata
if(extname == "ssl")
{
// check if this user has an swhois field to send
if(user->GetExt(extname, dummy))
{
// call this function in the linking module, let it format the data how it
// sees fit, and send it on its way. We dont need or want to know how.
proto->ProtoSendMetaData(opaque, TYPE_USER, user, extname, displayable ? "Enabled" : "ON");
}
}
}
virtual void OnDecodeMetaData(int target_type, void* target, const std::string &extname, const std::string &extdata)
{
// check if its our metadata key, and its associated with a user
if ((target_type == TYPE_USER) && (extname == "ssl"))
{
userrec* dest = (userrec*)target;
// if they dont already have an ssl flag, accept the remote server's
if (!dest->GetExt(extname, dummy))
{
dest->Extend(extname, "ON");
}
}
}
bool Handshake(issl_session* session)
{
int ret = gnutls_handshake(session->sess);
if (ret < 0)
{
if(ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED)
{
// Handshake needs resuming later, read() or write() would have blocked.
if(gnutls_record_get_direction(session->sess) == 0)
{
// gnutls_handshake() wants to read() again.
session->status = ISSL_HANDSHAKING_READ;
}
else
{
// gnutls_handshake() wants to write() again.
session->status = ISSL_HANDSHAKING_WRITE;
MakePollWrite(session);
}
}
else
{
// Handshake failed.
CloseSession(session);
session->status = ISSL_CLOSING;
}
return false;
}
else
{
// Handshake complete.
// This will do for setting the ssl flag...it could be done earlier if it's needed. But this seems neater.
userrec* extendme = ServerInstance->FindDescriptor(session->fd);
if (extendme)
{
if (!extendme->GetExt("ssl", dummy))
extendme->Extend("ssl", "ON");
}
// Change the seesion state
session->status = ISSL_HANDSHAKEN;
// Finish writing, if any left
MakePollWrite(session);
return true;
}
}
virtual void OnPostConnect(userrec* user)
{
// This occurs AFTER OnUserConnect so we can be sure the
// protocol module has propogated the NICK message.
if ((user->GetExt("ssl", dummy)) && (IS_LOCAL(user)))
{
// Tell whatever protocol module we're using that we need to inform other servers of this metadata NOW.
std::deque<std::string>* metadata = new std::deque<std::string>;
metadata->push_back(user->nick);
metadata->push_back("ssl"); // The metadata id
metadata->push_back("ON"); // The value to send
Event* event = new Event((char*)metadata,(Module*)this,"send_metadata");
event->Send(ServerInstance); // Trigger the event. We don't care what module picks it up.
DELETE(event);
DELETE(metadata);
VerifyCertificate(&sessions[user->GetFd()],user);
if (sessions[user->GetFd()].sess)
{
std::string cipher = gnutls_kx_get_name(gnutls_kx_get(sessions[user->GetFd()].sess));
cipher.append("-").append(gnutls_cipher_get_name(gnutls_cipher_get(sessions[user->GetFd()].sess))).append("-");
cipher.append(gnutls_mac_get_name(gnutls_mac_get(sessions[user->GetFd()].sess)));
user->WriteServ("NOTICE %s :*** You are connected using SSL cipher \"%s\"", user->nick, cipher.c_str());
}
}
}
void MakePollWrite(issl_session* session)
{
OnRawSocketWrite(session->fd, NULL, 0);
}
void CloseSession(issl_session* session)
{
if(session->sess)
{
gnutls_bye(session->sess, GNUTLS_SHUT_WR);
gnutls_deinit(session->sess);
}
if(session->inbuf)
{
delete[] session->inbuf;
}
session->outbuf.clear();
session->inbuf = NULL;
session->sess = NULL;
session->status = ISSL_NONE;
}
void VerifyCertificate(issl_session* session, Extensible* user)
{
if (!session->sess || !user)
return;
unsigned int status;
const gnutls_datum_t* cert_list;
int ret;
unsigned int cert_list_size;
gnutls_x509_crt_t cert;
char name[MAXBUF];
unsigned char digest[MAXBUF];
size_t digest_size = sizeof(digest);
size_t name_size = sizeof(name);
ssl_cert* certinfo = new ssl_cert;
user->Extend("ssl_cert",certinfo);
/* This verification function uses the trusted CAs in the credentials
* structure. So you must have installed one or more CA certificates.
*/
ret = gnutls_certificate_verify_peers2(session->sess, &status);
if (ret < 0)
{
certinfo->data.insert(std::make_pair("error",std::string(gnutls_strerror(ret))));
return;
}
if (status & GNUTLS_CERT_INVALID)
{
certinfo->data.insert(std::make_pair("invalid",ConvToStr(1)));
}
else
{
certinfo->data.insert(std::make_pair("invalid",ConvToStr(0)));
}
if (status & GNUTLS_CERT_SIGNER_NOT_FOUND)
{
certinfo->data.insert(std::make_pair("unknownsigner",ConvToStr(1)));
}
else
{
certinfo->data.insert(std::make_pair("unknownsigner",ConvToStr(0)));
}
if (status & GNUTLS_CERT_REVOKED)
{
certinfo->data.insert(std::make_pair("revoked",ConvToStr(1)));
}
else
{
certinfo->data.insert(std::make_pair("revoked",ConvToStr(0)));
}
if (status & GNUTLS_CERT_SIGNER_NOT_CA)
{
certinfo->data.insert(std::make_pair("trusted",ConvToStr(0)));
}
else
{
certinfo->data.insert(std::make_pair("trusted",ConvToStr(1)));
}
/* Up to here the process is the same for X.509 certificates and
* OpenPGP keys. From now on X.509 certificates are assumed. This can
* be easily extended to work with openpgp keys as well.
*/
if (gnutls_certificate_type_get(session->sess) != GNUTLS_CRT_X509)
{
certinfo->data.insert(std::make_pair("error","No X509 keys sent"));
return;
}
ret = gnutls_x509_crt_init(&cert);
if (ret < 0)
{
certinfo->data.insert(std::make_pair("error",gnutls_strerror(ret)));
return;
}
cert_list_size = 0;
cert_list = gnutls_certificate_get_peers(session->sess, &cert_list_size);
if (cert_list == NULL)
{
certinfo->data.insert(std::make_pair("error","No certificate was found"));
return;
}
/* This is not a real world example, since we only check the first
* certificate in the given chain.
*/
ret = gnutls_x509_crt_import(cert, &cert_list[0], GNUTLS_X509_FMT_DER);
if (ret < 0)
{
certinfo->data.insert(std::make_pair("error",gnutls_strerror(ret)));
return;
}
gnutls_x509_crt_get_dn(cert, name, &name_size);
certinfo->data.insert(std::make_pair("dn",name));
gnutls_x509_crt_get_issuer_dn(cert, name, &name_size);
certinfo->data.insert(std::make_pair("issuer",name));
if ((ret = gnutls_x509_crt_get_fingerprint(cert, GNUTLS_DIG_MD5, digest, &digest_size)) < 0)
{
certinfo->data.insert(std::make_pair("error",gnutls_strerror(ret)));
}
else
{
certinfo->data.insert(std::make_pair("fingerprint",irc::hex(digest, digest_size)));
}
/* Beware here we do not check for errors.
*/
if ((gnutls_x509_crt_get_expiration_time(cert) < time(0)) || (gnutls_x509_crt_get_activation_time(cert) > time(0)))
{
certinfo->data.insert(std::make_pair("error","Not activated, or expired certificate"));
}
gnutls_x509_crt_deinit(cert);
return;
}
};
MODULE_INIT(ModuleSSLGnuTLS);
\ No newline at end of file +/* +------------------------------------+ + * | 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 <gnutls/gnutls.h> +#include <gnutls/x509.h> + +#include "inspircd_config.h" +#include "configreader.h" +#include "users.h" +#include "channels.h" +#include "modules.h" +#include "socket.h" +#include "hashcomp.h" +#include "transport.h" + +#ifdef WINDOWS +#pragma comment(lib, "libgnutls-13.lib") +#undef MAX_DESCRIPTORS +#define MAX_DESCRIPTORS 10000 +#endif + +/* $ModDesc: Provides SSL support for clients */ +/* $CompileFlags: exec("libgnutls-config --cflags") */ +/* $LinkerFlags: rpath("libgnutls-config --libs") exec("libgnutls-config --libs") */ +/* $ModDep: transport.h */ + + +enum issl_status { ISSL_NONE, ISSL_HANDSHAKING_READ, ISSL_HANDSHAKING_WRITE, ISSL_HANDSHAKEN, ISSL_CLOSING, ISSL_CLOSED }; + +bool isin(int port, const std::vector<int> &portlist) +{ + for(unsigned int i = 0; i < portlist.size(); i++) + if(portlist[i] == port) + return true; + + return false; +} + +/** Represents an SSL user's extra data + */ +class issl_session : public classbase +{ +public: + gnutls_session_t sess; + issl_status status; + std::string outbuf; + int inbufoffset; + char* inbuf; + int fd; +}; + +class ModuleSSLGnuTLS : public Module +{ + + ConfigReader* Conf; + + char* dummy; + + std::vector<int> listenports; + + int inbufsize; + issl_session sessions[MAX_DESCRIPTORS]; + + gnutls_certificate_credentials x509_cred; + gnutls_dh_params dh_params; + + std::string keyfile; + std::string certfile; + std::string cafile; + std::string crlfile; + std::string sslports; + int dh_bits; + + int clientactive; + + public: + + ModuleSSLGnuTLS(InspIRCd* Me) + : Module(Me) + { + ServerInstance->PublishInterface("InspSocketHook", this); + + // Not rehashable...because I cba to reduce all the sizes of existing buffers. + inbufsize = ServerInstance->Config->NetBufferSize; + + gnutls_global_init(); // This must be called once in the program + + if(gnutls_certificate_allocate_credentials(&x509_cred) != 0) + ServerInstance->Log(DEFAULT, "m_ssl_gnutls.so: Failed to allocate certificate credentials"); + + // Guessing return meaning + if(gnutls_dh_params_init(&dh_params) < 0) + ServerInstance->Log(DEFAULT, "m_ssl_gnutls.so: Failed to initialise DH parameters"); + + // Needs the flag as it ignores a plain /rehash + OnRehash(NULL,"ssl"); + + // Void return, guess we assume success + gnutls_certificate_set_dh_params(x509_cred, dh_params); + } + + virtual void OnRehash(userrec* user, const std::string ¶m) + { + if(param != "ssl") + return; + + Conf = new ConfigReader(ServerInstance); + + for(unsigned int i = 0; i < listenports.size(); i++) + { + ServerInstance->Config->DelIOHook(listenports[i]); + } + + listenports.clear(); + clientactive = 0; + sslports.clear(); + + for(int i = 0; i < Conf->Enumerate("bind"); i++) + { + // For each <bind> tag + std::string x = Conf->ReadValue("bind", "type", i); + if(((x.empty()) || (x == "clients")) && (Conf->ReadValue("bind", "ssl", i) == "gnutls")) + { + // Get the port we're meant to be listening on with SSL + std::string port = Conf->ReadValue("bind", "port", i); + irc::portparser portrange(port, false); + long portno = -1; + while ((portno = portrange.GetToken())) + { + clientactive++; + try + { + if (ServerInstance->Config->AddIOHook(portno, this)) + { + listenports.push_back(portno); + for (size_t i = 0; i < ServerInstance->Config->ports.size(); i++) + if (ServerInstance->Config->ports[i]->GetPort() == portno) + ServerInstance->Config->ports[i]->SetDescription("ssl"); + ServerInstance->Log(DEFAULT, "m_ssl_gnutls.so: Enabling SSL for port %d", portno); + sslports.append("*:").append(ConvToStr(portno)).append(";"); + } + else + { + ServerInstance->Log(DEFAULT, "m_ssl_gnutls.so: FAILED to enable SSL on port %d, maybe you have another ssl or similar module loaded?", portno); + } + } + catch (ModuleException &e) + { + ServerInstance->Log(DEFAULT, "m_ssl_gnutls.so: FAILED to enable SSL on port %d: %s. Maybe it's already hooked by the same port on a different IP, or you have an other SSL or similar module loaded?", portno, e.GetReason()); + } + } + } + } + + std::string confdir(ServerInstance->ConfigFileName); + // +1 so we the path ends with a / + confdir = confdir.substr(0, confdir.find_last_of('/') + 1); + + cafile = Conf->ReadValue("gnutls", "cafile", 0); + crlfile = Conf->ReadValue("gnutls", "crlfile", 0); + certfile = Conf->ReadValue("gnutls", "certfile", 0); + keyfile = Conf->ReadValue("gnutls", "keyfile", 0); + dh_bits = Conf->ReadInteger("gnutls", "dhbits", 0, false); + + // Set all the default values needed. + if (cafile.empty()) + cafile = "ca.pem"; + + if (crlfile.empty()) + crlfile = "crl.pem"; + + if (certfile.empty()) + certfile = "cert.pem"; + + if (keyfile.empty()) + keyfile = "key.pem"; + + if((dh_bits != 768) && (dh_bits != 1024) && (dh_bits != 2048) && (dh_bits != 3072) && (dh_bits != 4096)) + dh_bits = 1024; + + // Prepend relative paths with the path to the config directory. + if(cafile[0] != '/') + cafile = confdir + cafile; + + if(crlfile[0] != '/') + crlfile = confdir + crlfile; + + if(certfile[0] != '/') + certfile = confdir + certfile; + + if(keyfile[0] != '/') + keyfile = confdir + keyfile; + + int ret; + + if((ret =gnutls_certificate_set_x509_trust_file(x509_cred, cafile.c_str(), GNUTLS_X509_FMT_PEM)) < 0) + ServerInstance->Log(DEFAULT, "m_ssl_gnutls.so: Failed to set X.509 trust file '%s': %s", cafile.c_str(), gnutls_strerror(ret)); + + if((ret = gnutls_certificate_set_x509_crl_file (x509_cred, crlfile.c_str(), GNUTLS_X509_FMT_PEM)) < 0) + ServerInstance->Log(DEFAULT, "m_ssl_gnutls.so: Failed to set X.509 CRL file '%s': %s", crlfile.c_str(), gnutls_strerror(ret)); + + if((ret = gnutls_certificate_set_x509_key_file (x509_cred, certfile.c_str(), keyfile.c_str(), GNUTLS_X509_FMT_PEM)) < 0) + { + // If this fails, no SSL port will work. At all. So, do the smart thing - throw a ModuleException + throw ModuleException("Unable to load GnuTLS server certificate: " + std::string(gnutls_strerror(ret))); + } + + // This may be on a large (once a day or week) timer eventually. + GenerateDHParams(); + + DELETE(Conf); + } + + void GenerateDHParams() + { + // Generate Diffie Hellman parameters - for use with DHE + // kx algorithms. These should be discarded and regenerated + // once a day, once a week or once a month. Depending on the + // security requirements. + + int ret; + + if((ret = gnutls_dh_params_generate2(dh_params, dh_bits)) < 0) + ServerInstance->Log(DEFAULT, "m_ssl_gnutls.so: Failed to generate DH parameters (%d bits): %s", dh_bits, gnutls_strerror(ret)); + } + + virtual ~ModuleSSLGnuTLS() + { + gnutls_dh_params_deinit(dh_params); + gnutls_certificate_free_credentials(x509_cred); + gnutls_global_deinit(); + } + + virtual void OnCleanup(int target_type, void* item) + { + if(target_type == TYPE_USER) + { + userrec* user = (userrec*)item; + + if(user->GetExt("ssl", dummy) && isin(user->GetPort(), listenports)) + { + // User is using SSL, they're a local user, and they're using one of *our* SSL ports. + // Potentially there could be multiple SSL modules loaded at once on different ports. + ServerInstance->GlobalCulls.AddItem(user, "SSL module unloading"); + } + if (user->GetExt("ssl_cert", dummy) && isin(user->GetPort(), listenports)) + { + ssl_cert* tofree; + user->GetExt("ssl_cert", tofree); + delete tofree; + user->Shrink("ssl_cert"); + } + } + } + + virtual void OnUnloadModule(Module* mod, const std::string &name) + { + if(mod == this) + { + for(unsigned int i = 0; i < listenports.size(); i++) + { + ServerInstance->Config->DelIOHook(listenports[i]); + for (size_t j = 0; j < ServerInstance->Config->ports.size(); j++) + if (ServerInstance->Config->ports[j]->GetPort() == listenports[i]) + ServerInstance->Config->ports[j]->SetDescription("plaintext"); + } + } + } + + virtual Version GetVersion() + { + return Version(1, 1, 0, 0, VF_VENDOR, API_VERSION); + } + + void Implements(char* List) + { + List[I_On005Numeric] = List[I_OnRawSocketConnect] = List[I_OnRawSocketAccept] = List[I_OnRawSocketClose] = List[I_OnRawSocketRead] = List[I_OnRawSocketWrite] = List[I_OnCleanup] = 1; + List[I_OnRequest] = List[I_OnSyncUserMetaData] = List[I_OnDecodeMetaData] = List[I_OnUnloadModule] = List[I_OnRehash] = List[I_OnWhois] = List[I_OnPostConnect] = 1; + } + + virtual void On005Numeric(std::string &output) + { + output.append(" SSL=" + sslports); + } + + virtual char* OnRequest(Request* request) + { + ISHRequest* ISR = (ISHRequest*)request; + if (strcmp("IS_NAME", request->GetId()) == 0) + { + return "gnutls"; + } + else if (strcmp("IS_HOOK", request->GetId()) == 0) + { + char* ret = "OK"; + try + { + ret = ServerInstance->Config->AddIOHook((Module*)this, (InspSocket*)ISR->Sock) ? (char*)"OK" : NULL; + } + catch (ModuleException &e) + { + return NULL; + } + return ret; + } + else if (strcmp("IS_UNHOOK", request->GetId()) == 0) + { + return ServerInstance->Config->DelIOHook((InspSocket*)ISR->Sock) ? (char*)"OK" : NULL; + } + else if (strcmp("IS_HSDONE", request->GetId()) == 0) + { + if (ISR->Sock->GetFd() < 0) + return (char*)"OK"; + + issl_session* session = &sessions[ISR->Sock->GetFd()]; + return (session->status == ISSL_HANDSHAKING_READ || session->status == ISSL_HANDSHAKING_WRITE) ? NULL : (char*)"OK"; + } + else if (strcmp("IS_ATTACH", request->GetId()) == 0) + { + if (ISR->Sock->GetFd() > -1) + { + issl_session* session = &sessions[ISR->Sock->GetFd()]; + if (session->sess) + { + if ((Extensible*)ServerInstance->FindDescriptor(ISR->Sock->GetFd()) == (Extensible*)(ISR->Sock)) + { + VerifyCertificate(session, (InspSocket*)ISR->Sock); + return "OK"; + } + } + } + } + return NULL; + } + + + virtual void OnRawSocketAccept(int fd, const std::string &ip, int localport) + { + issl_session* session = &sessions[fd]; + + session->fd = fd; + session->inbuf = new char[inbufsize]; + session->inbufoffset = 0; + + gnutls_init(&session->sess, GNUTLS_SERVER); + + gnutls_set_default_priority(session->sess); // Avoid calling all the priority functions, defaults are adequate. + gnutls_credentials_set(session->sess, GNUTLS_CRD_CERTIFICATE, x509_cred); + gnutls_dh_set_prime_bits(session->sess, dh_bits); + + /* This is an experimental change to avoid a warning on 64bit systems about casting between integer and pointer of different sizes + * This needs testing, but it's easy enough to rollback if need be + * Old: gnutls_transport_set_ptr(session->sess, (gnutls_transport_ptr_t) fd); // Give gnutls the fd for the socket. + * New: gnutls_transport_set_ptr(session->sess, &fd); // Give gnutls the fd for the socket. + * + * With testing this seems to...not work :/ + */ + + gnutls_transport_set_ptr(session->sess, (gnutls_transport_ptr_t) fd); // Give gnutls the fd for the socket. + + gnutls_certificate_server_set_request(session->sess, GNUTLS_CERT_REQUEST); // Request client certificate if any. + + Handshake(session); + } + + virtual void OnRawSocketConnect(int fd) + { + issl_session* session = &sessions[fd]; + + session->fd = fd; + session->inbuf = new char[inbufsize]; + session->inbufoffset = 0; + + gnutls_init(&session->sess, GNUTLS_CLIENT); + + gnutls_set_default_priority(session->sess); // Avoid calling all the priority functions, defaults are adequate. + gnutls_credentials_set(session->sess, GNUTLS_CRD_CERTIFICATE, x509_cred); + gnutls_dh_set_prime_bits(session->sess, dh_bits); + gnutls_transport_set_ptr(session->sess, (gnutls_transport_ptr_t) fd); // Give gnutls the fd for the socket. + + Handshake(session); + } + + virtual void OnRawSocketClose(int fd) + { + CloseSession(&sessions[fd]); + + EventHandler* user = ServerInstance->SE->GetRef(fd); + + if ((user) && (user->GetExt("ssl_cert", dummy))) + { + ssl_cert* tofree; + user->GetExt("ssl_cert", tofree); + delete tofree; + user->Shrink("ssl_cert"); + } + } + + virtual int OnRawSocketRead(int fd, char* buffer, unsigned int count, int &readresult) + { + issl_session* session = &sessions[fd]; + + if (!session->sess) + { + readresult = 0; + CloseSession(session); + return 1; + } + + if (session->status == ISSL_HANDSHAKING_READ) + { + // The handshake isn't finished, try to finish it. + + if(!Handshake(session)) + { + // Couldn't resume handshake. + return -1; + } + } + else if (session->status == ISSL_HANDSHAKING_WRITE) + { + errno = EAGAIN; + return -1; + } + + // If we resumed the handshake then session->status will be ISSL_HANDSHAKEN. + + if (session->status == ISSL_HANDSHAKEN) + { + // Is this right? Not sure if the unencrypted data is garaunteed to be the same length. + // Read into the inbuffer, offset from the beginning by the amount of data we have that insp hasn't taken yet. + int ret = gnutls_record_recv(session->sess, session->inbuf + session->inbufoffset, inbufsize - session->inbufoffset); + + if (ret == 0) + { + // Client closed connection. + readresult = 0; + CloseSession(session); + return 1; + } + else if (ret < 0) + { + if (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED) + { + errno = EAGAIN; + return -1; + } + else + { + readresult = 0; + CloseSession(session); + } + } + else + { + // Read successfully 'ret' bytes into inbuf + inbufoffset + // There are 'ret' + 'inbufoffset' bytes of data in 'inbuf' + // 'buffer' is 'count' long + + unsigned int length = ret + session->inbufoffset; + + if(count <= length) + { + memcpy(buffer, session->inbuf, count); + // Move the stuff left in inbuf to the beginning of it + memcpy(session->inbuf, session->inbuf + count, (length - count)); + // Now we need to set session->inbufoffset to the amount of data still waiting to be handed to insp. + session->inbufoffset = length - count; + // Insp uses readresult as the count of how much data there is in buffer, so: + readresult = count; + } + else + { + // There's not as much in the inbuf as there is space in the buffer, so just copy the whole thing. + memcpy(buffer, session->inbuf, length); + // Zero the offset, as there's nothing there.. + session->inbufoffset = 0; + // As above + readresult = length; + } + } + } + else if(session->status == ISSL_CLOSING) + readresult = 0; + + return 1; + } + + virtual int OnRawSocketWrite(int fd, const char* buffer, int count) + { + if (!count) + return 0; + + issl_session* session = &sessions[fd]; + const char* sendbuffer = buffer; + + if (!session->sess) + { + ServerInstance->Log(DEBUG,"No session"); + CloseSession(session); + return 1; + } + + session->outbuf.append(sendbuffer, count); + sendbuffer = session->outbuf.c_str(); + count = session->outbuf.size(); + + if (session->status == ISSL_HANDSHAKING_WRITE) + { + // The handshake isn't finished, try to finish it. + ServerInstance->Log(DEBUG,"Finishing handshake"); + Handshake(session); + errno = EAGAIN; + return -1; + } + + int ret = 0; + + if (session->status == ISSL_HANDSHAKEN) + { + ServerInstance->Log(DEBUG,"Send record"); + ret = gnutls_record_send(session->sess, sendbuffer, count); + ServerInstance->Log(DEBUG,"Return: %d", ret); + + if (ret == 0) + { + CloseSession(session); + } + else if (ret < 0) + { + if(ret != GNUTLS_E_AGAIN && ret != GNUTLS_E_INTERRUPTED) + { + ServerInstance->Log(DEBUG,"Not egain or interrupt, close session"); + CloseSession(session); + } + else + { + ServerInstance->Log(DEBUG,"Again please"); + errno = EAGAIN; + return -1; + } + } + else + { + ServerInstance->Log(DEBUG,"Trim buffer"); + session->outbuf = session->outbuf.substr(ret); + } + } + + /* Who's smart idea was it to return 1 when we havent written anything? + * This fucks the buffer up in InspSocket :p + */ + return ret < 1 ? 0 : ret; + } + + // :kenny.chatspike.net 320 Om Epy|AFK :is a Secure Connection + virtual void OnWhois(userrec* source, userrec* dest) + { + if (!clientactive) + return; + + // Bugfix, only send this numeric for *our* SSL users + if(dest->GetExt("ssl", dummy) || (IS_LOCAL(dest) && isin(dest->GetPort(), listenports))) + { + ServerInstance->SendWhoisLine(source, dest, 320, "%s %s :is using a secure connection", source->nick, dest->nick); + } + } + + virtual void OnSyncUserMetaData(userrec* user, Module* proto, void* opaque, const std::string &extname, bool displayable) + { + // check if the linking module wants to know about OUR metadata + if(extname == "ssl") + { + // check if this user has an swhois field to send + if(user->GetExt(extname, dummy)) + { + // call this function in the linking module, let it format the data how it + // sees fit, and send it on its way. We dont need or want to know how. + proto->ProtoSendMetaData(opaque, TYPE_USER, user, extname, displayable ? "Enabled" : "ON"); + } + } + } + + virtual void OnDecodeMetaData(int target_type, void* target, const std::string &extname, const std::string &extdata) + { + // check if its our metadata key, and its associated with a user + if ((target_type == TYPE_USER) && (extname == "ssl")) + { + userrec* dest = (userrec*)target; + // if they dont already have an ssl flag, accept the remote server's + if (!dest->GetExt(extname, dummy)) + { + dest->Extend(extname, "ON"); + } + } + } + + bool Handshake(issl_session* session) + { + int ret = gnutls_handshake(session->sess); + + if (ret < 0) + { + if(ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED) + { + // Handshake needs resuming later, read() or write() would have blocked. + + if(gnutls_record_get_direction(session->sess) == 0) + { + // gnutls_handshake() wants to read() again. + session->status = ISSL_HANDSHAKING_READ; + } + else + { + // gnutls_handshake() wants to write() again. + session->status = ISSL_HANDSHAKING_WRITE; + MakePollWrite(session); + } + } + else + { + // Handshake failed. + CloseSession(session); + session->status = ISSL_CLOSING; + } + + return false; + } + else + { + // Handshake complete. + // This will do for setting the ssl flag...it could be done earlier if it's needed. But this seems neater. + userrec* extendme = ServerInstance->FindDescriptor(session->fd); + if (extendme) + { + if (!extendme->GetExt("ssl", dummy)) + extendme->Extend("ssl", "ON"); + } + + // Change the seesion state + session->status = ISSL_HANDSHAKEN; + + // Finish writing, if any left + MakePollWrite(session); + + return true; + } + } + + virtual void OnPostConnect(userrec* user) + { + // This occurs AFTER OnUserConnect so we can be sure the + // protocol module has propogated the NICK message. + if ((user->GetExt("ssl", dummy)) && (IS_LOCAL(user))) + { + // Tell whatever protocol module we're using that we need to inform other servers of this metadata NOW. + std::deque<std::string>* metadata = new std::deque<std::string>; + metadata->push_back(user->nick); + metadata->push_back("ssl"); // The metadata id + metadata->push_back("ON"); // The value to send + Event* event = new Event((char*)metadata,(Module*)this,"send_metadata"); + event->Send(ServerInstance); // Trigger the event. We don't care what module picks it up. + DELETE(event); + DELETE(metadata); + + VerifyCertificate(&sessions[user->GetFd()],user); + if (sessions[user->GetFd()].sess) + { + std::string cipher = gnutls_kx_get_name(gnutls_kx_get(sessions[user->GetFd()].sess)); + cipher.append("-").append(gnutls_cipher_get_name(gnutls_cipher_get(sessions[user->GetFd()].sess))).append("-"); + cipher.append(gnutls_mac_get_name(gnutls_mac_get(sessions[user->GetFd()].sess))); + user->WriteServ("NOTICE %s :*** You are connected using SSL cipher \"%s\"", user->nick, cipher.c_str()); + } + } + } + + void MakePollWrite(issl_session* session) + { + OnRawSocketWrite(session->fd, NULL, 0); + } + + void CloseSession(issl_session* session) + { + if(session->sess) + { + gnutls_bye(session->sess, GNUTLS_SHUT_WR); + gnutls_deinit(session->sess); + } + + if(session->inbuf) + { + delete[] session->inbuf; + } + + session->outbuf.clear(); + session->inbuf = NULL; + session->sess = NULL; + session->status = ISSL_NONE; + } + + void VerifyCertificate(issl_session* session, Extensible* user) + { + if (!session->sess || !user) + return; + + unsigned int status; + const gnutls_datum_t* cert_list; + int ret; + unsigned int cert_list_size; + gnutls_x509_crt_t cert; + char name[MAXBUF]; + unsigned char digest[MAXBUF]; + size_t digest_size = sizeof(digest); + size_t name_size = sizeof(name); + ssl_cert* certinfo = new ssl_cert; + + user->Extend("ssl_cert",certinfo); + + /* This verification function uses the trusted CAs in the credentials + * structure. So you must have installed one or more CA certificates. + */ + ret = gnutls_certificate_verify_peers2(session->sess, &status); + + if (ret < 0) + { + certinfo->data.insert(std::make_pair("error",std::string(gnutls_strerror(ret)))); + return; + } + + if (status & GNUTLS_CERT_INVALID) + { + certinfo->data.insert(std::make_pair("invalid",ConvToStr(1))); + } + else + { + certinfo->data.insert(std::make_pair("invalid",ConvToStr(0))); + } + if (status & GNUTLS_CERT_SIGNER_NOT_FOUND) + { + certinfo->data.insert(std::make_pair("unknownsigner",ConvToStr(1))); + } + else + { + certinfo->data.insert(std::make_pair("unknownsigner",ConvToStr(0))); + } + if (status & GNUTLS_CERT_REVOKED) + { + certinfo->data.insert(std::make_pair("revoked",ConvToStr(1))); + } + else + { + certinfo->data.insert(std::make_pair("revoked",ConvToStr(0))); + } + if (status & GNUTLS_CERT_SIGNER_NOT_CA) + { + certinfo->data.insert(std::make_pair("trusted",ConvToStr(0))); + } + else + { + certinfo->data.insert(std::make_pair("trusted",ConvToStr(1))); + } + + /* Up to here the process is the same for X.509 certificates and + * OpenPGP keys. From now on X.509 certificates are assumed. This can + * be easily extended to work with openpgp keys as well. + */ + if (gnutls_certificate_type_get(session->sess) != GNUTLS_CRT_X509) + { + certinfo->data.insert(std::make_pair("error","No X509 keys sent")); + return; + } + + ret = gnutls_x509_crt_init(&cert); + if (ret < 0) + { + certinfo->data.insert(std::make_pair("error",gnutls_strerror(ret))); + return; + } + + cert_list_size = 0; + cert_list = gnutls_certificate_get_peers(session->sess, &cert_list_size); + if (cert_list == NULL) + { + certinfo->data.insert(std::make_pair("error","No certificate was found")); + return; + } + + /* This is not a real world example, since we only check the first + * certificate in the given chain. + */ + + ret = gnutls_x509_crt_import(cert, &cert_list[0], GNUTLS_X509_FMT_DER); + if (ret < 0) + { + certinfo->data.insert(std::make_pair("error",gnutls_strerror(ret))); + return; + } + + gnutls_x509_crt_get_dn(cert, name, &name_size); + + certinfo->data.insert(std::make_pair("dn",name)); + + gnutls_x509_crt_get_issuer_dn(cert, name, &name_size); + + certinfo->data.insert(std::make_pair("issuer",name)); + + if ((ret = gnutls_x509_crt_get_fingerprint(cert, GNUTLS_DIG_MD5, digest, &digest_size)) < 0) + { + certinfo->data.insert(std::make_pair("error",gnutls_strerror(ret))); + } + else + { + certinfo->data.insert(std::make_pair("fingerprint",irc::hex(digest, digest_size))); + } + + /* Beware here we do not check for errors. + */ + if ((gnutls_x509_crt_get_expiration_time(cert) < time(0)) || (gnutls_x509_crt_get_activation_time(cert) > time(0))) + { + certinfo->data.insert(std::make_pair("error","Not activated, or expired certificate")); + } + + gnutls_x509_crt_deinit(cert); + + return; + } + +}; + +MODULE_INIT(ModuleSSLGnuTLS); + diff --git a/src/modules/extra/m_ssl_openssl.cpp b/src/modules/extra/m_ssl_openssl.cpp index 43dc43aea..ffd9d4032 100644 --- a/src/modules/extra/m_ssl_openssl.cpp +++ b/src/modules/extra/m_ssl_openssl.cpp @@ -1 +1,901 @@ -/* +------------------------------------+
* | 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 <openssl/ssl.h>
#include <openssl/err.h>
#ifdef WINDOWS
#include <openssl/applink.c>
#endif
#include "configreader.h"
#include "users.h"
#include "channels.h"
#include "modules.h"
#include "socket.h"
#include "hashcomp.h"
#include "transport.h"
#ifdef WINDOWS
#pragma comment(lib, "libeay32MTd")
#pragma comment(lib, "ssleay32MTd")
#undef MAX_DESCRIPTORS
#define MAX_DESCRIPTORS 10000
#endif
/* $ModDesc: Provides SSL support for clients */
/* $CompileFlags: pkgconfversion("openssl","0.9.7") pkgconfincludes("openssl","/openssl/ssl.h","") */
/* $LinkerFlags: rpath("pkg-config --libs openssl") pkgconflibs("openssl","/libssl.so","-lssl -lcrypto -ldl") */
/* $ModDep: transport.h */
enum issl_status { ISSL_NONE, ISSL_HANDSHAKING, ISSL_OPEN };
enum issl_io_status { ISSL_WRITE, ISSL_READ };
static bool SelfSigned = false;
bool isin(int port, const std::vector<int> &portlist)
{
for(unsigned int i = 0; i < portlist.size(); i++)
if(portlist[i] == port)
return true;
return false;
}
char* get_error()
{
return ERR_error_string(ERR_get_error(), NULL);
}
static int error_callback(const char *str, size_t len, void *u);
/** Represents an SSL user's extra data
*/
class issl_session : public classbase
{
public:
SSL* sess;
issl_status status;
issl_io_status rstat;
issl_io_status wstat;
unsigned int inbufoffset;
char* inbuf; // Buffer OpenSSL reads into.
std::string outbuf; // Buffer for outgoing data that OpenSSL will not take.
int fd;
bool outbound;
issl_session()
{
outbound = false;
rstat = ISSL_READ;
wstat = ISSL_WRITE;
}
};
static int OnVerify(int preverify_ok, X509_STORE_CTX *ctx)
{
/* XXX: This will allow self signed certificates.
* In the future if we want an option to not allow this,
* we can just return preverify_ok here, and openssl
* will boot off self-signed and invalid peer certs.
*/
int ve = X509_STORE_CTX_get_error(ctx);
SelfSigned = (ve == X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT);
return 1;
}
class ModuleSSLOpenSSL : public Module
{
ConfigReader* Conf;
std::vector<int> listenports;
int inbufsize;
issl_session sessions[MAX_DESCRIPTORS];
SSL_CTX* ctx;
SSL_CTX* clictx;
char* dummy;
char cipher[MAXBUF];
std::string keyfile;
std::string certfile;
std::string cafile;
// std::string crlfile;
std::string dhfile;
std::string sslports;
int clientactive;
public:
InspIRCd* PublicInstance;
ModuleSSLOpenSSL(InspIRCd* Me)
: Module(Me), PublicInstance(Me)
{
ServerInstance->PublishInterface("InspSocketHook", this);
// Not rehashable...because I cba to reduce all the sizes of existing buffers.
inbufsize = ServerInstance->Config->NetBufferSize;
/* Global SSL library initialization*/
SSL_library_init();
SSL_load_error_strings();
/* Build our SSL contexts:
* NOTE: OpenSSL makes us have two contexts, one for servers and one for clients. ICK.
*/
ctx = SSL_CTX_new( SSLv23_server_method() );
clictx = SSL_CTX_new( SSLv23_client_method() );
SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE, OnVerify);
SSL_CTX_set_verify(clictx, SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE, OnVerify);
// Needs the flag as it ignores a plain /rehash
OnRehash(NULL,"ssl");
}
virtual void OnRehash(userrec* user, const std::string ¶m)
{
if (param != "ssl")
return;
Conf = new ConfigReader(ServerInstance);
for (unsigned int i = 0; i < listenports.size(); i++)
{
ServerInstance->Config->DelIOHook(listenports[i]);
}
listenports.clear();
clientactive = 0;
sslports.clear();
for (int i = 0; i < Conf->Enumerate("bind"); i++)
{
// For each <bind> tag
std::string x = Conf->ReadValue("bind", "type", i);
if (((x.empty()) || (x == "clients")) && (Conf->ReadValue("bind", "ssl", i) == "openssl"))
{
// Get the port we're meant to be listening on with SSL
std::string port = Conf->ReadValue("bind", "port", i);
irc::portparser portrange(port, false);
long portno = -1;
while ((portno = portrange.GetToken()))
{
clientactive++;
try
{
if (ServerInstance->Config->AddIOHook(portno, this))
{
listenports.push_back(portno);
for (size_t i = 0; i < ServerInstance->Config->ports.size(); i++)
if (ServerInstance->Config->ports[i]->GetPort() == portno)
ServerInstance->Config->ports[i]->SetDescription("ssl");
ServerInstance->Log(DEFAULT, "m_ssl_openssl.so: Enabling SSL for port %d", portno);
sslports.append("*:").append(ConvToStr(portno)).append(";");
}
else
{
ServerInstance->Log(DEFAULT, "m_ssl_openssl.so: FAILED to enable SSL on port %d, maybe you have another ssl or similar module loaded?", portno);
}
}
catch (ModuleException &e)
{
ServerInstance->Log(DEFAULT, "m_ssl_openssl.so: FAILED to enable SSL on port %d: %s. Maybe it's already hooked by the same port on a different IP, or you have another SSL or similar module loaded?", portno, e.GetReason());
}
}
}
}
if (!sslports.empty())
sslports.erase(sslports.end() - 1);
std::string confdir(ServerInstance->ConfigFileName);
// +1 so we the path ends with a /
confdir = confdir.substr(0, confdir.find_last_of('/') + 1);
cafile = Conf->ReadValue("openssl", "cafile", 0);
certfile = Conf->ReadValue("openssl", "certfile", 0);
keyfile = Conf->ReadValue("openssl", "keyfile", 0);
dhfile = Conf->ReadValue("openssl", "dhfile", 0);
// Set all the default values needed.
if (cafile.empty())
cafile = "ca.pem";
if (certfile.empty())
certfile = "cert.pem";
if (keyfile.empty())
keyfile = "key.pem";
if (dhfile.empty())
dhfile = "dhparams.pem";
// Prepend relative paths with the path to the config directory.
if (cafile[0] != '/')
cafile = confdir + cafile;
if (certfile[0] != '/')
certfile = confdir + certfile;
if (keyfile[0] != '/')
keyfile = confdir + keyfile;
if (dhfile[0] != '/')
dhfile = confdir + dhfile;
/* Load our keys and certificates
* NOTE: OpenSSL's error logging API sucks, don't blame us for this clusterfuck.
*/
if ((!SSL_CTX_use_certificate_chain_file(ctx, certfile.c_str())) || (!SSL_CTX_use_certificate_chain_file(clictx, certfile.c_str())))
{
ServerInstance->Log(DEFAULT, "m_ssl_openssl.so: Can't read certificate file %s. %s", certfile.c_str(), strerror(errno));
ERR_print_errors_cb(error_callback, this);
}
if (((!SSL_CTX_use_PrivateKey_file(ctx, keyfile.c_str(), SSL_FILETYPE_PEM))) || (!SSL_CTX_use_PrivateKey_file(clictx, keyfile.c_str(), SSL_FILETYPE_PEM)))
{
ServerInstance->Log(DEFAULT, "m_ssl_openssl.so: Can't read key file %s. %s", keyfile.c_str(), strerror(errno));
ERR_print_errors_cb(error_callback, this);
}
/* Load the CAs we trust*/
if (((!SSL_CTX_load_verify_locations(ctx, cafile.c_str(), 0))) || (!SSL_CTX_load_verify_locations(clictx, cafile.c_str(), 0)))
{
ServerInstance->Log(DEFAULT, "m_ssl_openssl.so: Can't read CA list from %s. %s", cafile.c_str(), strerror(errno));
ERR_print_errors_cb(error_callback, this);
}
FILE* dhpfile = fopen(dhfile.c_str(), "r");
DH* ret;
if (dhpfile == NULL)
{
ServerInstance->Log(DEFAULT, "m_ssl_openssl.so Couldn't open DH file %s: %s", dhfile.c_str(), strerror(errno));
throw ModuleException("Couldn't open DH file " + dhfile + ": " + strerror(errno));
}
else
{
ret = PEM_read_DHparams(dhpfile, NULL, NULL, NULL);
if ((SSL_CTX_set_tmp_dh(ctx, ret) < 0) || (SSL_CTX_set_tmp_dh(clictx, ret) < 0))
{
ServerInstance->Log(DEFAULT, "m_ssl_openssl.so: Couldn't set DH parameters %s. SSL errors follow:", dhfile.c_str());
ERR_print_errors_cb(error_callback, this);
}
}
fclose(dhpfile);
DELETE(Conf);
}
virtual void On005Numeric(std::string &output)
{
output.append(" SSL=" + sslports);
}
virtual ~ModuleSSLOpenSSL()
{
SSL_CTX_free(ctx);
SSL_CTX_free(clictx);
}
virtual void OnCleanup(int target_type, void* item)
{
if (target_type == TYPE_USER)
{
userrec* user = (userrec*)item;
if (user->GetExt("ssl", dummy) && IS_LOCAL(user) && isin(user->GetPort(), listenports))
{
// User is using SSL, they're a local user, and they're using one of *our* SSL ports.
// Potentially there could be multiple SSL modules loaded at once on different ports.
ServerInstance->GlobalCulls.AddItem(user, "SSL module unloading");
}
if (user->GetExt("ssl_cert", dummy) && isin(user->GetPort(), listenports))
{
ssl_cert* tofree;
user->GetExt("ssl_cert", tofree);
delete tofree;
user->Shrink("ssl_cert");
}
}
}
virtual void OnUnloadModule(Module* mod, const std::string &name)
{
if (mod == this)
{
for(unsigned int i = 0; i < listenports.size(); i++)
{
ServerInstance->Config->DelIOHook(listenports[i]);
for (size_t j = 0; j < ServerInstance->Config->ports.size(); j++)
if (ServerInstance->Config->ports[j]->GetPort() == listenports[i])
ServerInstance->Config->ports[j]->SetDescription("plaintext");
}
}
}
virtual Version GetVersion()
{
return Version(1, 1, 0, 0, VF_VENDOR, API_VERSION);
}
void Implements(char* List)
{
List[I_OnRawSocketConnect] = List[I_OnRawSocketAccept] = List[I_OnRawSocketClose] = List[I_OnRawSocketRead] = List[I_OnRawSocketWrite] = List[I_OnCleanup] = List[I_On005Numeric] = 1;
List[I_OnRequest] = List[I_OnSyncUserMetaData] = List[I_OnDecodeMetaData] = List[I_OnUnloadModule] = List[I_OnRehash] = List[I_OnWhois] = List[I_OnPostConnect] = 1;
}
virtual char* OnRequest(Request* request)
{
ISHRequest* ISR = (ISHRequest*)request;
if (strcmp("IS_NAME", request->GetId()) == 0)
{
return "openssl";
}
else if (strcmp("IS_HOOK", request->GetId()) == 0)
{
char* ret = "OK";
try
{
ret = ServerInstance->Config->AddIOHook((Module*)this, (InspSocket*)ISR->Sock) ? (char*)"OK" : NULL;
}
catch (ModuleException &e)
{
return NULL;
}
return ret;
}
else if (strcmp("IS_UNHOOK", request->GetId()) == 0)
{
return ServerInstance->Config->DelIOHook((InspSocket*)ISR->Sock) ? (char*)"OK" : NULL;
}
else if (strcmp("IS_HSDONE", request->GetId()) == 0)
{
ServerInstance->Log(DEBUG,"Module checking if handshake is done");
if (ISR->Sock->GetFd() < 0)
return (char*)"OK";
issl_session* session = &sessions[ISR->Sock->GetFd()];
return (session->status == ISSL_HANDSHAKING) ? NULL : (char*)"OK";
}
else if (strcmp("IS_ATTACH", request->GetId()) == 0)
{
issl_session* session = &sessions[ISR->Sock->GetFd()];
if (session->sess)
{
VerifyCertificate(session, (InspSocket*)ISR->Sock);
return "OK";
}
}
return NULL;
}
virtual void OnRawSocketAccept(int fd, const std::string &ip, int localport)
{
issl_session* session = &sessions[fd];
session->fd = fd;
session->inbuf = new char[inbufsize];
session->inbufoffset = 0;
session->sess = SSL_new(ctx);
session->status = ISSL_NONE;
session->outbound = false;
if (session->sess == NULL)
return;
if (SSL_set_fd(session->sess, fd) == 0)
{
ServerInstance->Log(DEBUG,"BUG: Can't set fd with SSL_set_fd: %d", fd);
return;
}
Handshake(session);
}
virtual void OnRawSocketConnect(int fd)
{
ServerInstance->Log(DEBUG,"OnRawSocketConnect connecting");
issl_session* session = &sessions[fd];
session->fd = fd;
session->inbuf = new char[inbufsize];
session->inbufoffset = 0;
session->sess = SSL_new(clictx);
session->status = ISSL_NONE;
session->outbound = true;
if (session->sess == NULL)
return;
if (SSL_set_fd(session->sess, fd) == 0)
{
ServerInstance->Log(DEBUG,"BUG: Can't set fd with SSL_set_fd: %d", fd);
return;
}
Handshake(session);
ServerInstance->Log(DEBUG,"Exiting OnRawSocketConnect");
}
virtual void OnRawSocketClose(int fd)
{
CloseSession(&sessions[fd]);
EventHandler* user = ServerInstance->SE->GetRef(fd);
if ((user) && (user->GetExt("ssl_cert", dummy)))
{
ssl_cert* tofree;
user->GetExt("ssl_cert", tofree);
delete tofree;
user->Shrink("ssl_cert");
}
}
virtual int OnRawSocketRead(int fd, char* buffer, unsigned int count, int &readresult)
{
issl_session* session = &sessions[fd];
ServerInstance->Log(DEBUG,"OnRawSocketRead");
if (!session->sess)
{
ServerInstance->Log(DEBUG,"OnRawSocketRead has no session");
readresult = 0;
CloseSession(session);
return 1;
}
if (session->status == ISSL_HANDSHAKING)
{
if (session->rstat == ISSL_READ || session->wstat == ISSL_READ)
{
ServerInstance->Log(DEBUG,"Resume handshake in read");
// The handshake isn't finished and it wants to read, try to finish it.
if (!Handshake(session))
{
ServerInstance->Log(DEBUG,"Cant resume handshake in read");
// Couldn't resume handshake.
return -1;
}
}
else
{
errno = EAGAIN;
return -1;
}
}
// If we resumed the handshake then session->status will be ISSL_OPEN
if (session->status == ISSL_OPEN)
{
if (session->wstat == ISSL_READ)
{
if(DoWrite(session) == 0)
return 0;
}
if (session->rstat == ISSL_READ)
{
int ret = DoRead(session);
if (ret > 0)
{
if (count <= session->inbufoffset)
{
memcpy(buffer, session->inbuf, count);
// Move the stuff left in inbuf to the beginning of it
memcpy(session->inbuf, session->inbuf + count, (session->inbufoffset - count));
// Now we need to set session->inbufoffset to the amount of data still waiting to be handed to insp.
session->inbufoffset -= count;
// Insp uses readresult as the count of how much data there is in buffer, so:
readresult = count;
}
else
{
// There's not as much in the inbuf as there is space in the buffer, so just copy the whole thing.
memcpy(buffer, session->inbuf, session->inbufoffset);
readresult = session->inbufoffset;
// Zero the offset, as there's nothing there..
session->inbufoffset = 0;
}
return 1;
}
else
{
return ret;
}
}
}
return -1;
}
virtual int OnRawSocketWrite(int fd, const char* buffer, int count)
{
issl_session* session = &sessions[fd];
if (!session->sess)
{
ServerInstance->Log(DEBUG,"Close session missing sess");
CloseSession(session);
return -1;
}
session->outbuf.append(buffer, count);
if (session->status == ISSL_HANDSHAKING)
{
// The handshake isn't finished, try to finish it.
if (session->rstat == ISSL_WRITE || session->wstat == ISSL_WRITE)
{
ServerInstance->Log(DEBUG,"Handshake resume");
Handshake(session);
}
}
if (session->status == ISSL_OPEN)
{
if (session->rstat == ISSL_WRITE)
{
ServerInstance->Log(DEBUG,"DoRead");
DoRead(session);
}
if (session->wstat == ISSL_WRITE)
{
ServerInstance->Log(DEBUG,"DoWrite");
return DoWrite(session);
}
}
return 1;
}
int DoWrite(issl_session* session)
{
if (!session->outbuf.size())
return -1;
int ret = SSL_write(session->sess, session->outbuf.data(), session->outbuf.size());
if (ret == 0)
{
ServerInstance->Log(DEBUG,"Oops, got 0 from SSL_write");
CloseSession(session);
return 0;
}
else if (ret < 0)
{
int err = SSL_get_error(session->sess, ret);
if (err == SSL_ERROR_WANT_WRITE)
{
session->wstat = ISSL_WRITE;
return -1;
}
else if (err == SSL_ERROR_WANT_READ)
{
session->wstat = ISSL_READ;
return -1;
}
else
{
ServerInstance->Log(DEBUG,"Close due to returned -1 in SSL_Write");
CloseSession(session);
return 0;
}
}
else
{
session->outbuf = session->outbuf.substr(ret);
return ret;
}
}
int DoRead(issl_session* session)
{
// Is this right? Not sure if the unencrypted data is garaunteed to be the same length.
// Read into the inbuffer, offset from the beginning by the amount of data we have that insp hasn't taken yet.
ServerInstance->Log(DEBUG,"DoRead");
int ret = SSL_read(session->sess, session->inbuf + session->inbufoffset, inbufsize - session->inbufoffset);
if (ret == 0)
{
// Client closed connection.
ServerInstance->Log(DEBUG,"Oops, got 0 from SSL_read");
CloseSession(session);
return 0;
}
else if (ret < 0)
{
int err = SSL_get_error(session->sess, ret);
if (err == SSL_ERROR_WANT_READ)
{
session->rstat = ISSL_READ;
ServerInstance->Log(DEBUG,"Setting want_read");
return -1;
}
else if (err == SSL_ERROR_WANT_WRITE)
{
session->rstat = ISSL_WRITE;
ServerInstance->Log(DEBUG,"Setting want_write");
return -1;
}
else
{
ServerInstance->Log(DEBUG,"Closed due to returned -1 in SSL_Read");
CloseSession(session);
return 0;
}
}
else
{
// Read successfully 'ret' bytes into inbuf + inbufoffset
// There are 'ret' + 'inbufoffset' bytes of data in 'inbuf'
// 'buffer' is 'count' long
session->inbufoffset += ret;
return ret;
}
}
// :kenny.chatspike.net 320 Om Epy|AFK :is a Secure Connection
virtual void OnWhois(userrec* source, userrec* dest)
{
if (!clientactive)
return;
// Bugfix, only send this numeric for *our* SSL users
if (dest->GetExt("ssl", dummy) || (IS_LOCAL(dest) && isin(dest->GetPort(), listenports)))
{
ServerInstance->SendWhoisLine(source, dest, 320, "%s %s :is using a secure connection", source->nick, dest->nick);
}
}
virtual void OnSyncUserMetaData(userrec* user, Module* proto, void* opaque, const std::string &extname, bool displayable)
{
// check if the linking module wants to know about OUR metadata
if (extname == "ssl")
{
// check if this user has an swhois field to send
if(user->GetExt(extname, dummy))
{
// call this function in the linking module, let it format the data how it
// sees fit, and send it on its way. We dont need or want to know how.
proto->ProtoSendMetaData(opaque, TYPE_USER, user, extname, displayable ? "Enabled" : "ON");
}
}
}
virtual void OnDecodeMetaData(int target_type, void* target, const std::string &extname, const std::string &extdata)
{
// check if its our metadata key, and its associated with a user
if ((target_type == TYPE_USER) && (extname == "ssl"))
{
userrec* dest = (userrec*)target;
// if they dont already have an ssl flag, accept the remote server's
if (!dest->GetExt(extname, dummy))
{
dest->Extend(extname, "ON");
}
}
}
bool Handshake(issl_session* session)
{
ServerInstance->Log(DEBUG,"Handshake");
int ret;
if (session->outbound)
{
ServerInstance->Log(DEBUG,"SSL_connect");
ret = SSL_connect(session->sess);
}
else
ret = SSL_accept(session->sess);
if (ret < 0)
{
int err = SSL_get_error(session->sess, ret);
if (err == SSL_ERROR_WANT_READ)
{
ServerInstance->Log(DEBUG,"Want read, handshaking");
session->rstat = ISSL_READ;
session->status = ISSL_HANDSHAKING;
return true;
}
else if (err == SSL_ERROR_WANT_WRITE)
{
ServerInstance->Log(DEBUG,"Want write, handshaking");
session->wstat = ISSL_WRITE;
session->status = ISSL_HANDSHAKING;
MakePollWrite(session);
return true;
}
else
{
ServerInstance->Log(DEBUG,"Handshake failed");
CloseSession(session);
}
return false;
}
else if (ret > 0)
{
// Handshake complete.
// This will do for setting the ssl flag...it could be done earlier if it's needed. But this seems neater.
userrec* u = ServerInstance->FindDescriptor(session->fd);
if (u)
{
if (!u->GetExt("ssl", dummy))
u->Extend("ssl", "ON");
}
session->status = ISSL_OPEN;
MakePollWrite(session);
return true;
}
else if (ret == 0)
{
int ssl_err = SSL_get_error(session->sess, ret);
char buf[1024];
ERR_print_errors_fp(stderr);
ServerInstance->Log(DEBUG,"Handshake fail 2: %d: %s", ssl_err, ERR_error_string(ssl_err,buf));
CloseSession(session);
return true;
}
return true;
}
virtual void OnPostConnect(userrec* user)
{
// This occurs AFTER OnUserConnect so we can be sure the
// protocol module has propogated the NICK message.
if ((user->GetExt("ssl", dummy)) && (IS_LOCAL(user)))
{
// Tell whatever protocol module we're using that we need to inform other servers of this metadata NOW.
std::deque<std::string>* metadata = new std::deque<std::string>;
metadata->push_back(user->nick);
metadata->push_back("ssl"); // The metadata id
metadata->push_back("ON"); // The value to send
Event* event = new Event((char*)metadata,(Module*)this,"send_metadata");
event->Send(ServerInstance); // Trigger the event. We don't care what module picks it up.
DELETE(event);
DELETE(metadata);
VerifyCertificate(&sessions[user->GetFd()], user);
if (sessions[user->GetFd()].sess)
user->WriteServ("NOTICE %s :*** You are connected using SSL cipher \"%s\"", user->nick, SSL_get_cipher(sessions[user->GetFd()].sess));
}
}
void MakePollWrite(issl_session* session)
{
OnRawSocketWrite(session->fd, NULL, 0);
//EventHandler* eh = ServerInstance->FindDescriptor(session->fd);
//if (eh)
// ServerInstance->SE->WantWrite(eh);
}
void CloseSession(issl_session* session)
{
if (session->sess)
{
SSL_shutdown(session->sess);
SSL_free(session->sess);
}
if (session->inbuf)
{
delete[] session->inbuf;
}
session->outbuf.clear();
session->inbuf = NULL;
session->sess = NULL;
session->status = ISSL_NONE;
}
void VerifyCertificate(issl_session* session, Extensible* user)
{
if (!session->sess || !user)
return;
X509* cert;
ssl_cert* certinfo = new ssl_cert;
unsigned int n;
unsigned char md[EVP_MAX_MD_SIZE];
const EVP_MD *digest = EVP_md5();
user->Extend("ssl_cert",certinfo);
cert = SSL_get_peer_certificate((SSL*)session->sess);
if (!cert)
{
certinfo->data.insert(std::make_pair("error","Could not get peer certificate: "+std::string(get_error())));
return;
}
certinfo->data.insert(std::make_pair("invalid", SSL_get_verify_result(session->sess) != X509_V_OK ? ConvToStr(1) : ConvToStr(0)));
if (SelfSigned)
{
certinfo->data.insert(std::make_pair("unknownsigner",ConvToStr(0)));
certinfo->data.insert(std::make_pair("trusted",ConvToStr(1)));
}
else
{
certinfo->data.insert(std::make_pair("unknownsigner",ConvToStr(1)));
certinfo->data.insert(std::make_pair("trusted",ConvToStr(0)));
}
certinfo->data.insert(std::make_pair("dn",std::string(X509_NAME_oneline(X509_get_subject_name(cert),0,0))));
certinfo->data.insert(std::make_pair("issuer",std::string(X509_NAME_oneline(X509_get_issuer_name(cert),0,0))));
if (!X509_digest(cert, digest, md, &n))
{
certinfo->data.insert(std::make_pair("error","Out of memory generating fingerprint"));
}
else
{
certinfo->data.insert(std::make_pair("fingerprint",irc::hex(md, n)));
}
if ((ASN1_UTCTIME_cmp_time_t(X509_get_notAfter(cert), time(NULL)) == -1) || (ASN1_UTCTIME_cmp_time_t(X509_get_notBefore(cert), time(NULL)) == 0))
{
certinfo->data.insert(std::make_pair("error","Not activated, or expired certificate"));
}
X509_free(cert);
}
};
static int error_callback(const char *str, size_t len, void *u)
{
ModuleSSLOpenSSL* mssl = (ModuleSSLOpenSSL*)u;
mssl->PublicInstance->Log(DEFAULT, "SSL error: " + std::string(str, len - 1));
return 0;
}
MODULE_INIT(ModuleSSLOpenSSL);
\ No newline at end of file +/* +------------------------------------+ + * | 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 <openssl/ssl.h> +#include <openssl/err.h> + +#ifdef WINDOWS +#include <openssl/applink.c> +#endif + +#include "configreader.h" +#include "users.h" +#include "channels.h" +#include "modules.h" + +#include "socket.h" +#include "hashcomp.h" + +#include "transport.h" + +#ifdef WINDOWS +#pragma comment(lib, "libeay32MTd") +#pragma comment(lib, "ssleay32MTd") +#undef MAX_DESCRIPTORS +#define MAX_DESCRIPTORS 10000 +#endif + +/* $ModDesc: Provides SSL support for clients */ +/* $CompileFlags: pkgconfversion("openssl","0.9.7") pkgconfincludes("openssl","/openssl/ssl.h","") */ +/* $LinkerFlags: rpath("pkg-config --libs openssl") pkgconflibs("openssl","/libssl.so","-lssl -lcrypto -ldl") */ +/* $ModDep: transport.h */ + +enum issl_status { ISSL_NONE, ISSL_HANDSHAKING, ISSL_OPEN }; +enum issl_io_status { ISSL_WRITE, ISSL_READ }; + +static bool SelfSigned = false; + +bool isin(int port, const std::vector<int> &portlist) +{ + for(unsigned int i = 0; i < portlist.size(); i++) + if(portlist[i] == port) + return true; + + return false; +} + +char* get_error() +{ + return ERR_error_string(ERR_get_error(), NULL); +} + +static int error_callback(const char *str, size_t len, void *u); + +/** Represents an SSL user's extra data + */ +class issl_session : public classbase +{ +public: + SSL* sess; + issl_status status; + issl_io_status rstat; + issl_io_status wstat; + + unsigned int inbufoffset; + char* inbuf; // Buffer OpenSSL reads into. + std::string outbuf; // Buffer for outgoing data that OpenSSL will not take. + int fd; + bool outbound; + + issl_session() + { + outbound = false; + rstat = ISSL_READ; + wstat = ISSL_WRITE; + } +}; + +static int OnVerify(int preverify_ok, X509_STORE_CTX *ctx) +{ + /* XXX: This will allow self signed certificates. + * In the future if we want an option to not allow this, + * we can just return preverify_ok here, and openssl + * will boot off self-signed and invalid peer certs. + */ + int ve = X509_STORE_CTX_get_error(ctx); + + SelfSigned = (ve == X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT); + + return 1; +} + +class ModuleSSLOpenSSL : public Module +{ + + ConfigReader* Conf; + + std::vector<int> listenports; + + int inbufsize; + issl_session sessions[MAX_DESCRIPTORS]; + + SSL_CTX* ctx; + SSL_CTX* clictx; + + char* dummy; + char cipher[MAXBUF]; + + std::string keyfile; + std::string certfile; + std::string cafile; + // std::string crlfile; + std::string dhfile; + std::string sslports; + + int clientactive; + + public: + + InspIRCd* PublicInstance; + + ModuleSSLOpenSSL(InspIRCd* Me) + : Module(Me), PublicInstance(Me) + { + ServerInstance->PublishInterface("InspSocketHook", this); + + // Not rehashable...because I cba to reduce all the sizes of existing buffers. + inbufsize = ServerInstance->Config->NetBufferSize; + + /* Global SSL library initialization*/ + SSL_library_init(); + SSL_load_error_strings(); + + /* Build our SSL contexts: + * NOTE: OpenSSL makes us have two contexts, one for servers and one for clients. ICK. + */ + ctx = SSL_CTX_new( SSLv23_server_method() ); + clictx = SSL_CTX_new( SSLv23_client_method() ); + + SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE, OnVerify); + SSL_CTX_set_verify(clictx, SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE, OnVerify); + + // Needs the flag as it ignores a plain /rehash + OnRehash(NULL,"ssl"); + } + + virtual void OnRehash(userrec* user, const std::string ¶m) + { + if (param != "ssl") + return; + + Conf = new ConfigReader(ServerInstance); + + for (unsigned int i = 0; i < listenports.size(); i++) + { + ServerInstance->Config->DelIOHook(listenports[i]); + } + + listenports.clear(); + clientactive = 0; + sslports.clear(); + + for (int i = 0; i < Conf->Enumerate("bind"); i++) + { + // For each <bind> tag + std::string x = Conf->ReadValue("bind", "type", i); + if (((x.empty()) || (x == "clients")) && (Conf->ReadValue("bind", "ssl", i) == "openssl")) + { + // Get the port we're meant to be listening on with SSL + std::string port = Conf->ReadValue("bind", "port", i); + irc::portparser portrange(port, false); + long portno = -1; + while ((portno = portrange.GetToken())) + { + clientactive++; + try + { + if (ServerInstance->Config->AddIOHook(portno, this)) + { + listenports.push_back(portno); + for (size_t i = 0; i < ServerInstance->Config->ports.size(); i++) + if (ServerInstance->Config->ports[i]->GetPort() == portno) + ServerInstance->Config->ports[i]->SetDescription("ssl"); + ServerInstance->Log(DEFAULT, "m_ssl_openssl.so: Enabling SSL for port %d", portno); + sslports.append("*:").append(ConvToStr(portno)).append(";"); + } + else + { + ServerInstance->Log(DEFAULT, "m_ssl_openssl.so: FAILED to enable SSL on port %d, maybe you have another ssl or similar module loaded?", portno); + } + } + catch (ModuleException &e) + { + ServerInstance->Log(DEFAULT, "m_ssl_openssl.so: FAILED to enable SSL on port %d: %s. Maybe it's already hooked by the same port on a different IP, or you have another SSL or similar module loaded?", portno, e.GetReason()); + } + } + } + } + + if (!sslports.empty()) + sslports.erase(sslports.end() - 1); + + std::string confdir(ServerInstance->ConfigFileName); + // +1 so we the path ends with a / + confdir = confdir.substr(0, confdir.find_last_of('/') + 1); + + cafile = Conf->ReadValue("openssl", "cafile", 0); + certfile = Conf->ReadValue("openssl", "certfile", 0); + keyfile = Conf->ReadValue("openssl", "keyfile", 0); + dhfile = Conf->ReadValue("openssl", "dhfile", 0); + + // Set all the default values needed. + if (cafile.empty()) + cafile = "ca.pem"; + + if (certfile.empty()) + certfile = "cert.pem"; + + if (keyfile.empty()) + keyfile = "key.pem"; + + if (dhfile.empty()) + dhfile = "dhparams.pem"; + + // Prepend relative paths with the path to the config directory. + if (cafile[0] != '/') + cafile = confdir + cafile; + + if (certfile[0] != '/') + certfile = confdir + certfile; + + if (keyfile[0] != '/') + keyfile = confdir + keyfile; + + if (dhfile[0] != '/') + dhfile = confdir + dhfile; + + /* Load our keys and certificates + * NOTE: OpenSSL's error logging API sucks, don't blame us for this clusterfuck. + */ + if ((!SSL_CTX_use_certificate_chain_file(ctx, certfile.c_str())) || (!SSL_CTX_use_certificate_chain_file(clictx, certfile.c_str()))) + { + ServerInstance->Log(DEFAULT, "m_ssl_openssl.so: Can't read certificate file %s. %s", certfile.c_str(), strerror(errno)); + ERR_print_errors_cb(error_callback, this); + } + + if (((!SSL_CTX_use_PrivateKey_file(ctx, keyfile.c_str(), SSL_FILETYPE_PEM))) || (!SSL_CTX_use_PrivateKey_file(clictx, keyfile.c_str(), SSL_FILETYPE_PEM))) + { + ServerInstance->Log(DEFAULT, "m_ssl_openssl.so: Can't read key file %s. %s", keyfile.c_str(), strerror(errno)); + ERR_print_errors_cb(error_callback, this); + } + + /* Load the CAs we trust*/ + if (((!SSL_CTX_load_verify_locations(ctx, cafile.c_str(), 0))) || (!SSL_CTX_load_verify_locations(clictx, cafile.c_str(), 0))) + { + ServerInstance->Log(DEFAULT, "m_ssl_openssl.so: Can't read CA list from %s. %s", cafile.c_str(), strerror(errno)); + ERR_print_errors_cb(error_callback, this); + } + + FILE* dhpfile = fopen(dhfile.c_str(), "r"); + DH* ret; + + if (dhpfile == NULL) + { + ServerInstance->Log(DEFAULT, "m_ssl_openssl.so Couldn't open DH file %s: %s", dhfile.c_str(), strerror(errno)); + throw ModuleException("Couldn't open DH file " + dhfile + ": " + strerror(errno)); + } + else + { + ret = PEM_read_DHparams(dhpfile, NULL, NULL, NULL); + if ((SSL_CTX_set_tmp_dh(ctx, ret) < 0) || (SSL_CTX_set_tmp_dh(clictx, ret) < 0)) + { + ServerInstance->Log(DEFAULT, "m_ssl_openssl.so: Couldn't set DH parameters %s. SSL errors follow:", dhfile.c_str()); + ERR_print_errors_cb(error_callback, this); + } + } + + fclose(dhpfile); + + DELETE(Conf); + } + + virtual void On005Numeric(std::string &output) + { + output.append(" SSL=" + sslports); + } + + virtual ~ModuleSSLOpenSSL() + { + SSL_CTX_free(ctx); + SSL_CTX_free(clictx); + } + + virtual void OnCleanup(int target_type, void* item) + { + if (target_type == TYPE_USER) + { + userrec* user = (userrec*)item; + + if (user->GetExt("ssl", dummy) && IS_LOCAL(user) && isin(user->GetPort(), listenports)) + { + // User is using SSL, they're a local user, and they're using one of *our* SSL ports. + // Potentially there could be multiple SSL modules loaded at once on different ports. + ServerInstance->GlobalCulls.AddItem(user, "SSL module unloading"); + } + if (user->GetExt("ssl_cert", dummy) && isin(user->GetPort(), listenports)) + { + ssl_cert* tofree; + user->GetExt("ssl_cert", tofree); + delete tofree; + user->Shrink("ssl_cert"); + } + } + } + + virtual void OnUnloadModule(Module* mod, const std::string &name) + { + if (mod == this) + { + for(unsigned int i = 0; i < listenports.size(); i++) + { + ServerInstance->Config->DelIOHook(listenports[i]); + for (size_t j = 0; j < ServerInstance->Config->ports.size(); j++) + if (ServerInstance->Config->ports[j]->GetPort() == listenports[i]) + ServerInstance->Config->ports[j]->SetDescription("plaintext"); + } + } + } + + virtual Version GetVersion() + { + return Version(1, 1, 0, 0, VF_VENDOR, API_VERSION); + } + + void Implements(char* List) + { + List[I_OnRawSocketConnect] = List[I_OnRawSocketAccept] = List[I_OnRawSocketClose] = List[I_OnRawSocketRead] = List[I_OnRawSocketWrite] = List[I_OnCleanup] = List[I_On005Numeric] = 1; + List[I_OnRequest] = List[I_OnSyncUserMetaData] = List[I_OnDecodeMetaData] = List[I_OnUnloadModule] = List[I_OnRehash] = List[I_OnWhois] = List[I_OnPostConnect] = 1; + } + + virtual char* OnRequest(Request* request) + { + ISHRequest* ISR = (ISHRequest*)request; + if (strcmp("IS_NAME", request->GetId()) == 0) + { + return "openssl"; + } + else if (strcmp("IS_HOOK", request->GetId()) == 0) + { + char* ret = "OK"; + try + { + ret = ServerInstance->Config->AddIOHook((Module*)this, (InspSocket*)ISR->Sock) ? (char*)"OK" : NULL; + } + catch (ModuleException &e) + { + return NULL; + } + + return ret; + } + else if (strcmp("IS_UNHOOK", request->GetId()) == 0) + { + return ServerInstance->Config->DelIOHook((InspSocket*)ISR->Sock) ? (char*)"OK" : NULL; + } + else if (strcmp("IS_HSDONE", request->GetId()) == 0) + { + ServerInstance->Log(DEBUG,"Module checking if handshake is done"); + if (ISR->Sock->GetFd() < 0) + return (char*)"OK"; + + issl_session* session = &sessions[ISR->Sock->GetFd()]; + return (session->status == ISSL_HANDSHAKING) ? NULL : (char*)"OK"; + } + else if (strcmp("IS_ATTACH", request->GetId()) == 0) + { + issl_session* session = &sessions[ISR->Sock->GetFd()]; + if (session->sess) + { + VerifyCertificate(session, (InspSocket*)ISR->Sock); + return "OK"; + } + } + return NULL; + } + + + virtual void OnRawSocketAccept(int fd, const std::string &ip, int localport) + { + issl_session* session = &sessions[fd]; + + session->fd = fd; + session->inbuf = new char[inbufsize]; + session->inbufoffset = 0; + session->sess = SSL_new(ctx); + session->status = ISSL_NONE; + session->outbound = false; + + if (session->sess == NULL) + return; + + if (SSL_set_fd(session->sess, fd) == 0) + { + ServerInstance->Log(DEBUG,"BUG: Can't set fd with SSL_set_fd: %d", fd); + return; + } + + Handshake(session); + } + + virtual void OnRawSocketConnect(int fd) + { + ServerInstance->Log(DEBUG,"OnRawSocketConnect connecting"); + issl_session* session = &sessions[fd]; + + session->fd = fd; + session->inbuf = new char[inbufsize]; + session->inbufoffset = 0; + session->sess = SSL_new(clictx); + session->status = ISSL_NONE; + session->outbound = true; + + if (session->sess == NULL) + return; + + if (SSL_set_fd(session->sess, fd) == 0) + { + ServerInstance->Log(DEBUG,"BUG: Can't set fd with SSL_set_fd: %d", fd); + return; + } + + Handshake(session); + ServerInstance->Log(DEBUG,"Exiting OnRawSocketConnect"); + } + + virtual void OnRawSocketClose(int fd) + { + CloseSession(&sessions[fd]); + + EventHandler* user = ServerInstance->SE->GetRef(fd); + + if ((user) && (user->GetExt("ssl_cert", dummy))) + { + ssl_cert* tofree; + user->GetExt("ssl_cert", tofree); + delete tofree; + user->Shrink("ssl_cert"); + } + } + + virtual int OnRawSocketRead(int fd, char* buffer, unsigned int count, int &readresult) + { + issl_session* session = &sessions[fd]; + + ServerInstance->Log(DEBUG,"OnRawSocketRead"); + + if (!session->sess) + { + ServerInstance->Log(DEBUG,"OnRawSocketRead has no session"); + readresult = 0; + CloseSession(session); + return 1; + } + + if (session->status == ISSL_HANDSHAKING) + { + if (session->rstat == ISSL_READ || session->wstat == ISSL_READ) + { + ServerInstance->Log(DEBUG,"Resume handshake in read"); + // The handshake isn't finished and it wants to read, try to finish it. + if (!Handshake(session)) + { + ServerInstance->Log(DEBUG,"Cant resume handshake in read"); + // Couldn't resume handshake. + return -1; + } + } + else + { + errno = EAGAIN; + return -1; + } + } + + // If we resumed the handshake then session->status will be ISSL_OPEN + + if (session->status == ISSL_OPEN) + { + if (session->wstat == ISSL_READ) + { + if(DoWrite(session) == 0) + return 0; + } + + if (session->rstat == ISSL_READ) + { + int ret = DoRead(session); + + if (ret > 0) + { + if (count <= session->inbufoffset) + { + memcpy(buffer, session->inbuf, count); + // Move the stuff left in inbuf to the beginning of it + memcpy(session->inbuf, session->inbuf + count, (session->inbufoffset - count)); + // Now we need to set session->inbufoffset to the amount of data still waiting to be handed to insp. + session->inbufoffset -= count; + // Insp uses readresult as the count of how much data there is in buffer, so: + readresult = count; + } + else + { + // There's not as much in the inbuf as there is space in the buffer, so just copy the whole thing. + memcpy(buffer, session->inbuf, session->inbufoffset); + + readresult = session->inbufoffset; + // Zero the offset, as there's nothing there.. + session->inbufoffset = 0; + } + + return 1; + } + else + { + return ret; + } + } + } + + return -1; + } + + virtual int OnRawSocketWrite(int fd, const char* buffer, int count) + { + issl_session* session = &sessions[fd]; + + if (!session->sess) + { + ServerInstance->Log(DEBUG,"Close session missing sess"); + CloseSession(session); + return -1; + } + + session->outbuf.append(buffer, count); + + if (session->status == ISSL_HANDSHAKING) + { + // The handshake isn't finished, try to finish it. + if (session->rstat == ISSL_WRITE || session->wstat == ISSL_WRITE) + { + ServerInstance->Log(DEBUG,"Handshake resume"); + Handshake(session); + } + } + + if (session->status == ISSL_OPEN) + { + if (session->rstat == ISSL_WRITE) + { + ServerInstance->Log(DEBUG,"DoRead"); + DoRead(session); + } + + if (session->wstat == ISSL_WRITE) + { + ServerInstance->Log(DEBUG,"DoWrite"); + return DoWrite(session); + } + } + + return 1; + } + + int DoWrite(issl_session* session) + { + if (!session->outbuf.size()) + return -1; + + int ret = SSL_write(session->sess, session->outbuf.data(), session->outbuf.size()); + + if (ret == 0) + { + ServerInstance->Log(DEBUG,"Oops, got 0 from SSL_write"); + CloseSession(session); + return 0; + } + else if (ret < 0) + { + int err = SSL_get_error(session->sess, ret); + + if (err == SSL_ERROR_WANT_WRITE) + { + session->wstat = ISSL_WRITE; + return -1; + } + else if (err == SSL_ERROR_WANT_READ) + { + session->wstat = ISSL_READ; + return -1; + } + else + { + ServerInstance->Log(DEBUG,"Close due to returned -1 in SSL_Write"); + CloseSession(session); + return 0; + } + } + else + { + session->outbuf = session->outbuf.substr(ret); + return ret; + } + } + + int DoRead(issl_session* session) + { + // Is this right? Not sure if the unencrypted data is garaunteed to be the same length. + // Read into the inbuffer, offset from the beginning by the amount of data we have that insp hasn't taken yet. + + ServerInstance->Log(DEBUG,"DoRead"); + + int ret = SSL_read(session->sess, session->inbuf + session->inbufoffset, inbufsize - session->inbufoffset); + + if (ret == 0) + { + // Client closed connection. + ServerInstance->Log(DEBUG,"Oops, got 0 from SSL_read"); + CloseSession(session); + return 0; + } + else if (ret < 0) + { + int err = SSL_get_error(session->sess, ret); + + if (err == SSL_ERROR_WANT_READ) + { + session->rstat = ISSL_READ; + ServerInstance->Log(DEBUG,"Setting want_read"); + return -1; + } + else if (err == SSL_ERROR_WANT_WRITE) + { + session->rstat = ISSL_WRITE; + ServerInstance->Log(DEBUG,"Setting want_write"); + return -1; + } + else + { + ServerInstance->Log(DEBUG,"Closed due to returned -1 in SSL_Read"); + CloseSession(session); + return 0; + } + } + else + { + // Read successfully 'ret' bytes into inbuf + inbufoffset + // There are 'ret' + 'inbufoffset' bytes of data in 'inbuf' + // 'buffer' is 'count' long + + session->inbufoffset += ret; + + return ret; + } + } + + // :kenny.chatspike.net 320 Om Epy|AFK :is a Secure Connection + virtual void OnWhois(userrec* source, userrec* dest) + { + if (!clientactive) + return; + + // Bugfix, only send this numeric for *our* SSL users + if (dest->GetExt("ssl", dummy) || (IS_LOCAL(dest) && isin(dest->GetPort(), listenports))) + { + ServerInstance->SendWhoisLine(source, dest, 320, "%s %s :is using a secure connection", source->nick, dest->nick); + } + } + + virtual void OnSyncUserMetaData(userrec* user, Module* proto, void* opaque, const std::string &extname, bool displayable) + { + // check if the linking module wants to know about OUR metadata + if (extname == "ssl") + { + // check if this user has an swhois field to send + if(user->GetExt(extname, dummy)) + { + // call this function in the linking module, let it format the data how it + // sees fit, and send it on its way. We dont need or want to know how. + proto->ProtoSendMetaData(opaque, TYPE_USER, user, extname, displayable ? "Enabled" : "ON"); + } + } + } + + virtual void OnDecodeMetaData(int target_type, void* target, const std::string &extname, const std::string &extdata) + { + // check if its our metadata key, and its associated with a user + if ((target_type == TYPE_USER) && (extname == "ssl")) + { + userrec* dest = (userrec*)target; + // if they dont already have an ssl flag, accept the remote server's + if (!dest->GetExt(extname, dummy)) + { + dest->Extend(extname, "ON"); + } + } + } + + bool Handshake(issl_session* session) + { + ServerInstance->Log(DEBUG,"Handshake"); + int ret; + + if (session->outbound) + { + ServerInstance->Log(DEBUG,"SSL_connect"); + ret = SSL_connect(session->sess); + } + else + ret = SSL_accept(session->sess); + + if (ret < 0) + { + int err = SSL_get_error(session->sess, ret); + + if (err == SSL_ERROR_WANT_READ) + { + ServerInstance->Log(DEBUG,"Want read, handshaking"); + session->rstat = ISSL_READ; + session->status = ISSL_HANDSHAKING; + return true; + } + else if (err == SSL_ERROR_WANT_WRITE) + { + ServerInstance->Log(DEBUG,"Want write, handshaking"); + session->wstat = ISSL_WRITE; + session->status = ISSL_HANDSHAKING; + MakePollWrite(session); + return true; + } + else + { + ServerInstance->Log(DEBUG,"Handshake failed"); + CloseSession(session); + } + + return false; + } + else if (ret > 0) + { + // Handshake complete. + // This will do for setting the ssl flag...it could be done earlier if it's needed. But this seems neater. + userrec* u = ServerInstance->FindDescriptor(session->fd); + if (u) + { + if (!u->GetExt("ssl", dummy)) + u->Extend("ssl", "ON"); + } + + session->status = ISSL_OPEN; + + MakePollWrite(session); + + return true; + } + else if (ret == 0) + { + int ssl_err = SSL_get_error(session->sess, ret); + char buf[1024]; + ERR_print_errors_fp(stderr); + ServerInstance->Log(DEBUG,"Handshake fail 2: %d: %s", ssl_err, ERR_error_string(ssl_err,buf)); + CloseSession(session); + return true; + } + + return true; + } + + virtual void OnPostConnect(userrec* user) + { + // This occurs AFTER OnUserConnect so we can be sure the + // protocol module has propogated the NICK message. + if ((user->GetExt("ssl", dummy)) && (IS_LOCAL(user))) + { + // Tell whatever protocol module we're using that we need to inform other servers of this metadata NOW. + std::deque<std::string>* metadata = new std::deque<std::string>; + metadata->push_back(user->nick); + metadata->push_back("ssl"); // The metadata id + metadata->push_back("ON"); // The value to send + Event* event = new Event((char*)metadata,(Module*)this,"send_metadata"); + event->Send(ServerInstance); // Trigger the event. We don't care what module picks it up. + DELETE(event); + DELETE(metadata); + + VerifyCertificate(&sessions[user->GetFd()], user); + if (sessions[user->GetFd()].sess) + user->WriteServ("NOTICE %s :*** You are connected using SSL cipher \"%s\"", user->nick, SSL_get_cipher(sessions[user->GetFd()].sess)); + } + } + + void MakePollWrite(issl_session* session) + { + OnRawSocketWrite(session->fd, NULL, 0); + //EventHandler* eh = ServerInstance->FindDescriptor(session->fd); + //if (eh) + // ServerInstance->SE->WantWrite(eh); + } + + void CloseSession(issl_session* session) + { + if (session->sess) + { + SSL_shutdown(session->sess); + SSL_free(session->sess); + } + + if (session->inbuf) + { + delete[] session->inbuf; + } + + session->outbuf.clear(); + session->inbuf = NULL; + session->sess = NULL; + session->status = ISSL_NONE; + } + + void VerifyCertificate(issl_session* session, Extensible* user) + { + if (!session->sess || !user) + return; + + X509* cert; + ssl_cert* certinfo = new ssl_cert; + unsigned int n; + unsigned char md[EVP_MAX_MD_SIZE]; + const EVP_MD *digest = EVP_md5(); + + user->Extend("ssl_cert",certinfo); + + cert = SSL_get_peer_certificate((SSL*)session->sess); + + if (!cert) + { + certinfo->data.insert(std::make_pair("error","Could not get peer certificate: "+std::string(get_error()))); + return; + } + + certinfo->data.insert(std::make_pair("invalid", SSL_get_verify_result(session->sess) != X509_V_OK ? ConvToStr(1) : ConvToStr(0))); + + if (SelfSigned) + { + certinfo->data.insert(std::make_pair("unknownsigner",ConvToStr(0))); + certinfo->data.insert(std::make_pair("trusted",ConvToStr(1))); + } + else + { + certinfo->data.insert(std::make_pair("unknownsigner",ConvToStr(1))); + certinfo->data.insert(std::make_pair("trusted",ConvToStr(0))); + } + + certinfo->data.insert(std::make_pair("dn",std::string(X509_NAME_oneline(X509_get_subject_name(cert),0,0)))); + certinfo->data.insert(std::make_pair("issuer",std::string(X509_NAME_oneline(X509_get_issuer_name(cert),0,0)))); + + if (!X509_digest(cert, digest, md, &n)) + { + certinfo->data.insert(std::make_pair("error","Out of memory generating fingerprint")); + } + else + { + certinfo->data.insert(std::make_pair("fingerprint",irc::hex(md, n))); + } + + if ((ASN1_UTCTIME_cmp_time_t(X509_get_notAfter(cert), time(NULL)) == -1) || (ASN1_UTCTIME_cmp_time_t(X509_get_notBefore(cert), time(NULL)) == 0)) + { + certinfo->data.insert(std::make_pair("error","Not activated, or expired certificate")); + } + + X509_free(cert); + } +}; + +static int error_callback(const char *str, size_t len, void *u) +{ + ModuleSSLOpenSSL* mssl = (ModuleSSLOpenSSL*)u; + mssl->PublicInstance->Log(DEFAULT, "SSL error: " + std::string(str, len - 1)); + return 0; +} + +MODULE_INIT(ModuleSSLOpenSSL); + diff --git a/src/modules/extra/m_ssl_oper_cert.cpp b/src/modules/extra/m_ssl_oper_cert.cpp index 7b1c90868..c67b50c8c 100644 --- a/src/modules/extra/m_ssl_oper_cert.cpp +++ b/src/modules/extra/m_ssl_oper_cert.cpp @@ -1 +1,180 @@ -/* +------------------------------------+
* | 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.
*
* ---------------------------------------------------
*/
/* $ModDesc: Allows for MD5 encrypted oper passwords */
/* $ModDep: transport.h */
#include "inspircd.h"
#include "inspircd_config.h"
#include "users.h"
#include "channels.h"
#include "modules.h"
#include "transport.h"
#include "wildcard.h"
/** Handle /FINGERPRINT
*/
class cmd_fingerprint : public command_t
{
public:
cmd_fingerprint (InspIRCd* Instance) : command_t(Instance,"FINGERPRINT", 0, 1)
{
this->source = "m_ssl_oper_cert.so";
syntax = "<nickname>";
}
CmdResult Handle (const char** parameters, int pcnt, userrec *user)
{
userrec* target = ServerInstance->FindNick(parameters[0]);
if (target)
{
ssl_cert* cert;
if (target->GetExt("ssl_cert",cert))
{
if (cert->GetFingerprint().length())
{
user->WriteServ("NOTICE %s :Certificate fingerprint for %s is %s",user->nick,target->nick,cert->GetFingerprint().c_str());
return CMD_SUCCESS;
}
else
{
user->WriteServ("NOTICE %s :Certificate fingerprint for %s does not exist!", user->nick,target->nick);
return CMD_FAILURE;
}
}
else
{
user->WriteServ("NOTICE %s :Certificate fingerprint for %s does not exist!", user->nick, target->nick);
return CMD_FAILURE;
}
}
else
{
user->WriteServ("401 %s %s :No such nickname", user->nick, parameters[0]);
return CMD_FAILURE;
}
}
};
class ModuleOperSSLCert : public Module
{
ssl_cert* cert;
bool HasCert;
cmd_fingerprint* mycommand;
ConfigReader* cf;
public:
ModuleOperSSLCert(InspIRCd* Me)
: Module(Me)
{
mycommand = new cmd_fingerprint(ServerInstance);
ServerInstance->AddCommand(mycommand);
cf = new ConfigReader(ServerInstance);
}
virtual ~ModuleOperSSLCert()
{
delete cf;
}
void Implements(char* List)
{
List[I_OnPreCommand] = List[I_OnRehash] = 1;
}
virtual void OnRehash(userrec* user, const std::string ¶meter)
{
delete cf;
cf = new ConfigReader(ServerInstance);
}
bool OneOfMatches(const char* host, const char* ip, const char* hostlist)
{
std::stringstream hl(hostlist);
std::string xhost;
while (hl >> xhost)
{
if (match(host,xhost.c_str()) || match(ip,xhost.c_str(),true))
{
return true;
}
}
return false;
}
virtual int OnPreCommand(const std::string &command, const char** parameters, int pcnt, userrec *user, bool validated, const std::string &original_line)
{
irc::string cmd = command.c_str();
if ((cmd == "OPER") && (validated))
{
char TheHost[MAXBUF];
char TheIP[MAXBUF];
std::string LoginName;
std::string Password;
std::string OperType;
std::string HostName;
std::string FingerPrint;
bool SSLOnly;
char* dummy;
snprintf(TheHost,MAXBUF,"%s@%s",user->ident,user->host);
snprintf(TheIP, MAXBUF,"%s@%s",user->ident,user->GetIPString());
HasCert = user->GetExt("ssl_cert",cert);
for (int i = 0; i < cf->Enumerate("oper"); i++)
{
LoginName = cf->ReadValue("oper", "name", i);
Password = cf->ReadValue("oper", "password", i);
OperType = cf->ReadValue("oper", "type", i);
HostName = cf->ReadValue("oper", "host", i);
FingerPrint = cf->ReadValue("oper", "fingerprint", i);
SSLOnly = cf->ReadFlag("oper", "sslonly", i);
if (SSLOnly || !FingerPrint.empty())
{
if ((!strcmp(LoginName.c_str(),parameters[0])) && (!ServerInstance->OperPassCompare(Password.c_str(),parameters[1],i)) && (OneOfMatches(TheHost,TheIP,HostName.c_str())))
{
if (SSLOnly && !user->GetExt("ssl", dummy))
{
user->WriteServ("491 %s :This oper login name requires an SSL connection.", user->nick);
return 1;
}
/* This oper would match */
if ((!cert) || (cert->GetFingerprint() != FingerPrint))
{
user->WriteServ("491 %s :This oper login name requires a matching key fingerprint.",user->nick);
ServerInstance->SNO->WriteToSnoMask('o',"'%s' cannot oper, does not match fingerprint", user->nick);
ServerInstance->Log(DEFAULT,"OPER: Failed oper attempt by %s!%s@%s: credentials valid, but wrong fingerprint.",user->nick,user->ident,user->host);
return 1;
}
}
}
}
}
return 0;
}
virtual Version GetVersion()
{
return Version(1,1,0,0,VF_VENDOR,API_VERSION);
}
};
MODULE_INIT(ModuleOperSSLCert);
\ No newline at end of file +/* +------------------------------------+ + * | 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. + * + * --------------------------------------------------- + */ + +/* $ModDesc: Allows for MD5 encrypted oper passwords */ +/* $ModDep: transport.h */ + +#include "inspircd.h" +#include "inspircd_config.h" +#include "users.h" +#include "channels.h" +#include "modules.h" +#include "transport.h" +#include "wildcard.h" + +/** Handle /FINGERPRINT + */ +class cmd_fingerprint : public command_t +{ + public: + cmd_fingerprint (InspIRCd* Instance) : command_t(Instance,"FINGERPRINT", 0, 1) + { + this->source = "m_ssl_oper_cert.so"; + syntax = "<nickname>"; + } + + CmdResult Handle (const char** parameters, int pcnt, userrec *user) + { + userrec* target = ServerInstance->FindNick(parameters[0]); + if (target) + { + ssl_cert* cert; + if (target->GetExt("ssl_cert",cert)) + { + if (cert->GetFingerprint().length()) + { + user->WriteServ("NOTICE %s :Certificate fingerprint for %s is %s",user->nick,target->nick,cert->GetFingerprint().c_str()); + return CMD_SUCCESS; + } + else + { + user->WriteServ("NOTICE %s :Certificate fingerprint for %s does not exist!", user->nick,target->nick); + return CMD_FAILURE; + } + } + else + { + user->WriteServ("NOTICE %s :Certificate fingerprint for %s does not exist!", user->nick, target->nick); + return CMD_FAILURE; + } + } + else + { + user->WriteServ("401 %s %s :No such nickname", user->nick, parameters[0]); + return CMD_FAILURE; + } + } +}; + + + +class ModuleOperSSLCert : public Module +{ + ssl_cert* cert; + bool HasCert; + cmd_fingerprint* mycommand; + ConfigReader* cf; + public: + + ModuleOperSSLCert(InspIRCd* Me) + : Module(Me) + { + mycommand = new cmd_fingerprint(ServerInstance); + ServerInstance->AddCommand(mycommand); + cf = new ConfigReader(ServerInstance); + } + + virtual ~ModuleOperSSLCert() + { + delete cf; + } + + void Implements(char* List) + { + List[I_OnPreCommand] = List[I_OnRehash] = 1; + } + + virtual void OnRehash(userrec* user, const std::string ¶meter) + { + delete cf; + cf = new ConfigReader(ServerInstance); + } + + bool OneOfMatches(const char* host, const char* ip, const char* hostlist) + { + std::stringstream hl(hostlist); + std::string xhost; + while (hl >> xhost) + { + if (match(host,xhost.c_str()) || match(ip,xhost.c_str(),true)) + { + return true; + } + } + return false; + } + + + virtual int OnPreCommand(const std::string &command, const char** parameters, int pcnt, userrec *user, bool validated, const std::string &original_line) + { + irc::string cmd = command.c_str(); + + if ((cmd == "OPER") && (validated)) + { + char TheHost[MAXBUF]; + char TheIP[MAXBUF]; + std::string LoginName; + std::string Password; + std::string OperType; + std::string HostName; + std::string FingerPrint; + bool SSLOnly; + char* dummy; + + snprintf(TheHost,MAXBUF,"%s@%s",user->ident,user->host); + snprintf(TheIP, MAXBUF,"%s@%s",user->ident,user->GetIPString()); + + HasCert = user->GetExt("ssl_cert",cert); + + for (int i = 0; i < cf->Enumerate("oper"); i++) + { + LoginName = cf->ReadValue("oper", "name", i); + Password = cf->ReadValue("oper", "password", i); + OperType = cf->ReadValue("oper", "type", i); + HostName = cf->ReadValue("oper", "host", i); + FingerPrint = cf->ReadValue("oper", "fingerprint", i); + SSLOnly = cf->ReadFlag("oper", "sslonly", i); + + if (SSLOnly || !FingerPrint.empty()) + { + if ((!strcmp(LoginName.c_str(),parameters[0])) && (!ServerInstance->OperPassCompare(Password.c_str(),parameters[1],i)) && (OneOfMatches(TheHost,TheIP,HostName.c_str()))) + { + if (SSLOnly && !user->GetExt("ssl", dummy)) + { + user->WriteServ("491 %s :This oper login name requires an SSL connection.", user->nick); + return 1; + } + + /* This oper would match */ + if ((!cert) || (cert->GetFingerprint() != FingerPrint)) + { + user->WriteServ("491 %s :This oper login name requires a matching key fingerprint.",user->nick); + ServerInstance->SNO->WriteToSnoMask('o',"'%s' cannot oper, does not match fingerprint", user->nick); + ServerInstance->Log(DEFAULT,"OPER: Failed oper attempt by %s!%s@%s: credentials valid, but wrong fingerprint.",user->nick,user->ident,user->host); + return 1; + } + } + } + } + } + return 0; + } + + virtual Version GetVersion() + { + return Version(1,1,0,0,VF_VENDOR,API_VERSION); + } +}; + +MODULE_INIT(ModuleOperSSLCert); + diff --git a/src/modules/extra/m_sslinfo.cpp b/src/modules/extra/m_sslinfo.cpp index 83de798c8..dc9274f1e 100644 --- a/src/modules/extra/m_sslinfo.cpp +++ b/src/modules/extra/m_sslinfo.cpp @@ -1 +1,94 @@ -/* +------------------------------------+
* | 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 "users.h"
#include "channels.h"
#include "modules.h"
#include "transport.h"
#include "wildcard.h"
#include "dns.h"
/* $ModDesc: Provides /sslinfo command used to test who a mask matches */
/* $ModDep: transport.h */
/** Handle /SSLINFO
*/
class cmd_sslinfo : public command_t
{
public:
cmd_sslinfo (InspIRCd* Instance) : command_t(Instance,"SSLINFO", 0, 1)
{
this->source = "m_sslinfo.so";
this->syntax = "<nick>";
}
CmdResult Handle (const char** parameters, int pcnt, userrec *user)
{
userrec* target = ServerInstance->FindNick(parameters[0]);
ssl_cert* cert;
if (target)
{
if (target->GetExt("ssl_cert", cert))
{
if (cert->GetError().length())
{
user->WriteServ("NOTICE %s :*** Error: %s", user->nick, cert->GetError().c_str());
}
user->WriteServ("NOTICE %s :*** Distinguised Name: %s", user->nick, cert->GetDN().c_str());
user->WriteServ("NOTICE %s :*** Issuer: %s", user->nick, cert->GetIssuer().c_str());
user->WriteServ("NOTICE %s :*** Key Fingerprint: %s", user->nick, cert->GetFingerprint().c_str());
return CMD_SUCCESS;
}
else
{
user->WriteServ("NOTICE %s :*** No SSL certificate information for this user.", user->nick);
return CMD_FAILURE;
}
}
else
user->WriteServ("401 %s %s :No such nickname", user->nick, parameters[0]);
return CMD_FAILURE;
}
};
class ModuleSSLInfo : public Module
{
cmd_sslinfo* newcommand;
public:
ModuleSSLInfo(InspIRCd* Me)
: Module(Me)
{
newcommand = new cmd_sslinfo(ServerInstance);
ServerInstance->AddCommand(newcommand);
}
void Implements(char* List)
{
}
virtual ~ModuleSSLInfo()
{
}
virtual Version GetVersion()
{
return Version(1, 1, 0, 0, VF_VENDOR, API_VERSION);
}
};
MODULE_INIT(ModuleSSLInfo);
\ No newline at end of file +/* +------------------------------------+ + * | 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 "users.h" +#include "channels.h" +#include "modules.h" +#include "transport.h" +#include "wildcard.h" +#include "dns.h" + +/* $ModDesc: Provides /sslinfo command used to test who a mask matches */ +/* $ModDep: transport.h */ + +/** Handle /SSLINFO + */ +class cmd_sslinfo : public command_t +{ + public: + cmd_sslinfo (InspIRCd* Instance) : command_t(Instance,"SSLINFO", 0, 1) + { + this->source = "m_sslinfo.so"; + this->syntax = "<nick>"; + } + + CmdResult Handle (const char** parameters, int pcnt, userrec *user) + { + userrec* target = ServerInstance->FindNick(parameters[0]); + ssl_cert* cert; + + if (target) + { + if (target->GetExt("ssl_cert", cert)) + { + if (cert->GetError().length()) + { + user->WriteServ("NOTICE %s :*** Error: %s", user->nick, cert->GetError().c_str()); + } + user->WriteServ("NOTICE %s :*** Distinguised Name: %s", user->nick, cert->GetDN().c_str()); + user->WriteServ("NOTICE %s :*** Issuer: %s", user->nick, cert->GetIssuer().c_str()); + user->WriteServ("NOTICE %s :*** Key Fingerprint: %s", user->nick, cert->GetFingerprint().c_str()); + return CMD_SUCCESS; + } + else + { + user->WriteServ("NOTICE %s :*** No SSL certificate information for this user.", user->nick); + return CMD_FAILURE; + } + } + else + user->WriteServ("401 %s %s :No such nickname", user->nick, parameters[0]); + + return CMD_FAILURE; + } +}; + +class ModuleSSLInfo : public Module +{ + cmd_sslinfo* newcommand; + public: + ModuleSSLInfo(InspIRCd* Me) + : Module(Me) + { + + newcommand = new cmd_sslinfo(ServerInstance); + ServerInstance->AddCommand(newcommand); + } + + void Implements(char* List) + { + } + + virtual ~ModuleSSLInfo() + { + } + + virtual Version GetVersion() + { + return Version(1, 1, 0, 0, VF_VENDOR, API_VERSION); + } +}; + +MODULE_INIT(ModuleSSLInfo); + diff --git a/src/modules/extra/m_testclient.cpp b/src/modules/extra/m_testclient.cpp index a867dad20..f4e58b7b5 100644 --- a/src/modules/extra/m_testclient.cpp +++ b/src/modules/extra/m_testclient.cpp @@ -1 +1,110 @@ -/* +------------------------------------+
* | 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 "users.h"
#include "channels.h"
#include "modules.h"
#include "configreader.h"
#include "m_sqlv2.h"
class ModuleTestClient : public Module
{
private:
public:
ModuleTestClient(InspIRCd* Me)
: Module::Module(Me)
{
}
void Implements(char* List)
{
List[I_OnRequest] = List[I_OnBackgroundTimer] = 1;
}
virtual Version GetVersion()
{
return Version(1, 1, 0, 0, VF_VENDOR, API_VERSION);
}
virtual void OnBackgroundTimer(time_t foo)
{
Module* target = ServerInstance->FindFeature("SQL");
if(target)
{
SQLrequest foo = SQLreq(this, target, "foo", "UPDATE rawr SET foo = '?' WHERE bar = 42", ConvToStr(time(NULL)));
if(foo.Send())
{
ServerInstance->Log(DEBUG, "Sent query, got given ID %lu", foo.id);
}
else
{
ServerInstance->Log(DEBUG, "SQLrequest failed: %s", foo.error.Str());
}
}
}
virtual char* OnRequest(Request* request)
{
if(strcmp(SQLRESID, request->GetId()) == 0)
{
ServerInstance->Log(DEBUG, "Got SQL result (%s)", request->GetId());
SQLresult* res = (SQLresult*)request;
if (res->error.Id() == NO_ERROR)
{
if(res->Cols())
{
ServerInstance->Log(DEBUG, "Got result with %d rows and %d columns", res->Rows(), res->Cols());
for (int r = 0; r < res->Rows(); r++)
{
ServerInstance->Log(DEBUG, "Row %d:", r);
for(int i = 0; i < res->Cols(); i++)
{
ServerInstance->Log(DEBUG, "\t[%s]: %s", res->ColName(i).c_str(), res->GetValue(r, i).d.c_str());
}
}
}
else
{
ServerInstance->Log(DEBUG, "%d rows affected in query", res->Rows());
}
}
else
{
ServerInstance->Log(DEBUG, "SQLrequest failed: %s", res->error.Str());
}
return SQLSUCCESS;
}
ServerInstance->Log(DEBUG, "Got unsupported API version string: %s", request->GetId());
return NULL;
}
virtual ~ModuleTestClient()
{
}
};
MODULE_INIT(ModuleTestClient);
\ No newline at end of file +/* +------------------------------------+ + * | 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 "users.h" +#include "channels.h" +#include "modules.h" +#include "configreader.h" +#include "m_sqlv2.h" + +class ModuleTestClient : public Module +{ +private: + + +public: + ModuleTestClient(InspIRCd* Me) + : Module::Module(Me) + { + } + + void Implements(char* List) + { + List[I_OnRequest] = List[I_OnBackgroundTimer] = 1; + } + + virtual Version GetVersion() + { + return Version(1, 1, 0, 0, VF_VENDOR, API_VERSION); + } + + virtual void OnBackgroundTimer(time_t foo) + { + Module* target = ServerInstance->FindFeature("SQL"); + + if(target) + { + SQLrequest foo = SQLreq(this, target, "foo", "UPDATE rawr SET foo = '?' WHERE bar = 42", ConvToStr(time(NULL))); + + if(foo.Send()) + { + ServerInstance->Log(DEBUG, "Sent query, got given ID %lu", foo.id); + } + else + { + ServerInstance->Log(DEBUG, "SQLrequest failed: %s", foo.error.Str()); + } + } + } + + virtual char* OnRequest(Request* request) + { + if(strcmp(SQLRESID, request->GetId()) == 0) + { + ServerInstance->Log(DEBUG, "Got SQL result (%s)", request->GetId()); + + SQLresult* res = (SQLresult*)request; + + if (res->error.Id() == NO_ERROR) + { + if(res->Cols()) + { + ServerInstance->Log(DEBUG, "Got result with %d rows and %d columns", res->Rows(), res->Cols()); + + for (int r = 0; r < res->Rows(); r++) + { + ServerInstance->Log(DEBUG, "Row %d:", r); + + for(int i = 0; i < res->Cols(); i++) + { + ServerInstance->Log(DEBUG, "\t[%s]: %s", res->ColName(i).c_str(), res->GetValue(r, i).d.c_str()); + } + } + } + else + { + ServerInstance->Log(DEBUG, "%d rows affected in query", res->Rows()); + } + } + else + { + ServerInstance->Log(DEBUG, "SQLrequest failed: %s", res->error.Str()); + + } + + return SQLSUCCESS; + } + + ServerInstance->Log(DEBUG, "Got unsupported API version string: %s", request->GetId()); + + return NULL; + } + + virtual ~ModuleTestClient() + { + } +}; + +MODULE_INIT(ModuleTestClient); + diff --git a/src/modules/extra/m_ziplink.cpp b/src/modules/extra/m_ziplink.cpp index 2a127258d..e815d1042 100644 --- a/src/modules/extra/m_ziplink.cpp +++ b/src/modules/extra/m_ziplink.cpp @@ -1 +1,452 @@ -/* +------------------------------------+
* | 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 <zlib.h>
#include "users.h"
#include "channels.h"
#include "modules.h"
#include "socket.h"
#include "hashcomp.h"
#include "transport.h"
/* $ModDesc: Provides zlib link support for servers */
/* $LinkerFlags: -lz */
/* $ModDep: transport.h */
/*
* Compressed data is transmitted across the link in the following format:
*
* 0 1 2 3 4 ... n
* +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
* | n | Z0 -> Zn |
* +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
*
* Where: n is the size of a frame, in network byte order, 4 bytes.
* Z0 through Zn are Zlib compressed data, n bytes in length.
*
* If the module fails to read the entire frame, then it will buffer
* the portion of the last frame it received, then attempt to read
* the next part of the frame next time a write notification arrives.
*
* ZLIB_BEST_COMPRESSION (9) is used for all sending of data with
* a flush after each frame. A frame may contain multiple lines
* and should be treated as raw binary data.
*
*/
/* Status of a connection */
enum izip_status { IZIP_OPEN, IZIP_CLOSED };
/* Maximum transfer size per read operation */
const unsigned int CHUNK = 128 * 1024;
/* This class manages a compressed chunk of data preceeded by
* a length count.
*
* It can handle having multiple chunks of data in the buffer
* at any time.
*/
class CountedBuffer : public classbase
{
std::string buffer; /* Current buffer contents */
unsigned int amount_expected; /* Amount of data expected */
public:
CountedBuffer()
{
amount_expected = 0;
}
/** Adds arbitrary compressed data to the buffer.
* - Binsry safe, of course.
*/
void AddData(unsigned char* data, int data_length)
{
buffer.append((const char*)data, data_length);
this->NextFrameSize();
}
/** Works out the size of the next compressed frame
*/
void NextFrameSize()
{
if ((!amount_expected) && (buffer.length() >= 4))
{
/* We have enough to read an int -
* Yes, this is safe, but its ugly. Give me
* a nicer way to read 4 bytes from a binary
* stream, and push them into a 32 bit int,
* and i'll consider replacing this.
*/
amount_expected = ntohl((buffer[3] << 24) | (buffer[2] << 16) | (buffer[1] << 8) | buffer[0]);
buffer = buffer.substr(4);
}
}
/** Gets the next frame and returns its size, or returns
* zero if there isnt one available yet.
* A frame can contain multiple plaintext lines.
* - Binary safe.
*/
int GetFrame(unsigned char* frame, int maxsize)
{
if (amount_expected)
{
/* We know how much we're expecting...
* Do we have enough yet?
*/
if (buffer.length() >= amount_expected)
{
int j = 0;
for (unsigned int i = 0; i < amount_expected; i++, j++)
frame[i] = buffer[i];
buffer = buffer.substr(j);
amount_expected = 0;
NextFrameSize();
return j;
}
}
/* Not enough for a frame yet, COME AGAIN! */
return 0;
}
};
/** Represents an zipped connections extra data
*/
class izip_session : public classbase
{
public:
z_stream c_stream; /* compression stream */
z_stream d_stream; /* decompress stream */
izip_status status; /* Connection status */
int fd; /* File descriptor */
CountedBuffer* inbuf; /* Holds input buffer */
std::string outbuf; /* Holds output buffer */
};
class ModuleZLib : public Module
{
izip_session sessions[MAX_DESCRIPTORS];
/* Used for stats z extensions */
float total_out_compressed;
float total_in_compressed;
float total_out_uncompressed;
float total_in_uncompressed;
public:
ModuleZLib(InspIRCd* Me)
: Module::Module(Me)
{
ServerInstance->PublishInterface("InspSocketHook", this);
total_out_compressed = total_in_compressed = 0;
total_out_uncompressed = total_out_uncompressed = 0;
}
virtual ~ModuleZLib()
{
ServerInstance->UnpublishInterface("InspSocketHook", this);
}
virtual Version GetVersion()
{
return Version(1, 1, 0, 0, VF_VENDOR, API_VERSION);
}
void Implements(char* List)
{
List[I_OnRawSocketConnect] = List[I_OnRawSocketAccept] = List[I_OnRawSocketClose] = List[I_OnRawSocketRead] = List[I_OnRawSocketWrite] = 1;
List[I_OnStats] = List[I_OnRequest] = 1;
}
/* Handle InspSocketHook API requests */
virtual char* OnRequest(Request* request)
{
ISHRequest* ISR = (ISHRequest*)request;
if (strcmp("IS_NAME", request->GetId()) == 0)
{
/* Return name */
return "zip";
}
else if (strcmp("IS_HOOK", request->GetId()) == 0)
{
/* Attach to an inspsocket */
char* ret = "OK";
try
{
ret = ServerInstance->Config->AddIOHook((Module*)this, (InspSocket*)ISR->Sock) ? (char*)"OK" : NULL;
}
catch (ModuleException& e)
{
return NULL;
}
return ret;
}
else if (strcmp("IS_UNHOOK", request->GetId()) == 0)
{
/* Detatch from an inspsocket */
return ServerInstance->Config->DelIOHook((InspSocket*)ISR->Sock) ? (char*)"OK" : NULL;
}
else if (strcmp("IS_HSDONE", request->GetId()) == 0)
{
/* Check for completion of handshake
* (actually, this module doesnt handshake)
*/
return "OK";
}
else if (strcmp("IS_ATTACH", request->GetId()) == 0)
{
/* Attach certificate data to the inspsocket
* (this module doesnt do that, either)
*/
return NULL;
}
return NULL;
}
/* Handle stats z (misc stats) */
virtual int OnStats(char symbol, userrec* user, string_list &results)
{
if (symbol == 'z')
{
std::string sn = ServerInstance->Config->ServerName;
/* Yeah yeah, i know, floats are ew.
* We used them here because we'd be casting to float anyway to do this maths,
* and also only floating point numbers can deal with the pretty large numbers
* involved in the total throughput of a server over a large period of time.
* (we dont count 64 bit ints because not all systems have 64 bit ints, and floats
* can still hold more.
*/
float outbound_r = 100 - ((total_out_compressed / (total_out_uncompressed + 0.001)) * 100);
float inbound_r = 100 - ((total_in_compressed / (total_in_uncompressed + 0.001)) * 100);
float total_compressed = total_in_compressed + total_out_compressed;
float total_uncompressed = total_in_uncompressed + total_out_uncompressed;
float total_r = 100 - ((total_compressed / (total_uncompressed + 0.001)) * 100);
char outbound_ratio[MAXBUF], inbound_ratio[MAXBUF], combined_ratio[MAXBUF];
sprintf(outbound_ratio, "%3.2f%%", outbound_r);
sprintf(inbound_ratio, "%3.2f%%", inbound_r);
sprintf(combined_ratio, "%3.2f%%", total_r);
results.push_back(sn+" 304 "+user->nick+" :ZIPSTATS outbound_compressed = "+ConvToStr(total_out_compressed));
results.push_back(sn+" 304 "+user->nick+" :ZIPSTATS inbound_compressed = "+ConvToStr(total_in_compressed));
results.push_back(sn+" 304 "+user->nick+" :ZIPSTATS outbound_uncompressed = "+ConvToStr(total_out_uncompressed));
results.push_back(sn+" 304 "+user->nick+" :ZIPSTATS inbound_uncompressed = "+ConvToStr(total_in_uncompressed));
results.push_back(sn+" 304 "+user->nick+" :ZIPSTATS outbound_ratio = "+outbound_ratio);
results.push_back(sn+" 304 "+user->nick+" :ZIPSTATS inbound_ratio = "+inbound_ratio);
results.push_back(sn+" 304 "+user->nick+" :ZIPSTATS combined_ratio = "+combined_ratio);
return 0;
}
return 0;
}
virtual void OnRawSocketAccept(int fd, const std::string &ip, int localport)
{
izip_session* session = &sessions[fd];
/* allocate state and buffers */
session->fd = fd;
session->status = IZIP_OPEN;
session->inbuf = new CountedBuffer();
session->c_stream.zalloc = (alloc_func)0;
session->c_stream.zfree = (free_func)0;
session->c_stream.opaque = (voidpf)0;
session->d_stream.zalloc = (alloc_func)0;
session->d_stream.zfree = (free_func)0;
session->d_stream.opaque = (voidpf)0;
}
virtual void OnRawSocketConnect(int fd)
{
/* Nothing special needs doing here compared to accept() */
OnRawSocketAccept(fd, "", 0);
}
virtual void OnRawSocketClose(int fd)
{
CloseSession(&sessions[fd]);
}
virtual int OnRawSocketRead(int fd, char* buffer, unsigned int count, int &readresult)
{
/* Find the sockets session */
izip_session* session = &sessions[fd];
if (session->status == IZIP_CLOSED)
return 0;
unsigned char compr[CHUNK + 4];
unsigned int offset = 0;
unsigned int total_size = 0;
/* Read CHUNK bytes at a time to the buffer (usually 128k) */
readresult = read(fd, compr, CHUNK);
/* Did we get anything? */
if (readresult > 0)
{
/* Add it to the frame queue */
session->inbuf->AddData(compr, readresult);
total_in_compressed += readresult;
/* Parse all completed frames */
int size = 0;
while ((size = session->inbuf->GetFrame(compr, CHUNK)) != 0)
{
session->d_stream.next_in = (Bytef*)compr;
session->d_stream.avail_in = 0;
session->d_stream.next_out = (Bytef*)(buffer + offset);
/* If we cant call this, well, we're boned. */
if (inflateInit(&session->d_stream) != Z_OK)
return 0;
while ((session->d_stream.total_out < count) && (session->d_stream.total_in < (unsigned int)size))
{
session->d_stream.avail_in = session->d_stream.avail_out = 1;
if (inflate(&session->d_stream, Z_NO_FLUSH) == Z_STREAM_END)
break;
}
/* Stick a fork in me, i'm done */
inflateEnd(&session->d_stream);
/* Update counters and offsets */
total_size += session->d_stream.total_out;
total_in_uncompressed += session->d_stream.total_out;
offset += session->d_stream.total_out;
}
/* Null-terminate the buffer -- this doesnt harm binary data */
buffer[total_size] = 0;
/* Set the read size to the correct total size */
readresult = total_size;
}
return (readresult > 0);
}
virtual int OnRawSocketWrite(int fd, const char* buffer, int count)
{
izip_session* session = &sessions[fd];
int ocount = count;
if (!count) /* Nothing to do! */
return 0;
if(session->status != IZIP_OPEN)
{
/* Seriously, wtf? */
CloseSession(session);
return 0;
}
unsigned char compr[CHUNK + 4];
/* Gentlemen, start your engines! */
if (deflateInit(&session->c_stream, Z_BEST_COMPRESSION) != Z_OK)
{
CloseSession(session);
return 0;
}
/* Set buffer sizes (we reserve 4 bytes at the start of the
* buffer for the length counters)
*/
session->c_stream.next_in = (Bytef*)buffer;
session->c_stream.next_out = compr + 4;
/* Compress the text */
while ((session->c_stream.total_in < (unsigned int)count) && (session->c_stream.total_out < CHUNK))
{
session->c_stream.avail_in = session->c_stream.avail_out = 1;
if (deflate(&session->c_stream, Z_NO_FLUSH) != Z_OK)
{
CloseSession(session);
return 0;
}
}
/* Finish the stream */
for (session->c_stream.avail_out = 1; deflate(&session->c_stream, Z_FINISH) != Z_STREAM_END; session->c_stream.avail_out = 1);
deflateEnd(&session->c_stream);
total_out_uncompressed += ocount;
total_out_compressed += session->c_stream.total_out;
/** Assemble the frame length onto the frame, in network byte order */
compr[0] = (session->c_stream.total_out >> 24);
compr[1] = (session->c_stream.total_out >> 16);
compr[2] = (session->c_stream.total_out >> 8);
compr[3] = (session->c_stream.total_out & 0xFF);
/* Add compressed data plus leading length to the output buffer -
* Note, we may have incomplete half-sent frames in here.
*/
session->outbuf.append((const char*)compr, session->c_stream.total_out + 4);
/* Lets see how much we can send out */
int ret = write(fd, session->outbuf.data(), session->outbuf.length());
/* Check for errors, and advance the buffer if any was sent */
if (ret > 0)
session->outbuf = session->outbuf.substr(ret);
else if (ret < 1)
{
if (ret == -1)
{
if (errno == EAGAIN)
return 0;
else
{
session->outbuf.clear();
return 0;
}
}
else
{
session->outbuf.clear();
return 0;
}
}
/* ALL LIES the lot of it, we havent really written
* this amount, but the layer above doesnt need to know.
*/
return ocount;
}
void CloseSession(izip_session* session)
{
if (session->status == IZIP_OPEN)
{
session->status = IZIP_CLOSED;
session->outbuf.clear();
delete session->inbuf;
}
}
};
MODULE_INIT(ModuleZLib);
\ No newline at end of file +/* +------------------------------------+ + * | 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 <zlib.h> +#include "users.h" +#include "channels.h" +#include "modules.h" +#include "socket.h" +#include "hashcomp.h" +#include "transport.h" + +/* $ModDesc: Provides zlib link support for servers */ +/* $LinkerFlags: -lz */ +/* $ModDep: transport.h */ + +/* + * Compressed data is transmitted across the link in the following format: + * + * 0 1 2 3 4 ... n + * +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ + * | n | Z0 -> Zn | + * +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ + * + * Where: n is the size of a frame, in network byte order, 4 bytes. + * Z0 through Zn are Zlib compressed data, n bytes in length. + * + * If the module fails to read the entire frame, then it will buffer + * the portion of the last frame it received, then attempt to read + * the next part of the frame next time a write notification arrives. + * + * ZLIB_BEST_COMPRESSION (9) is used for all sending of data with + * a flush after each frame. A frame may contain multiple lines + * and should be treated as raw binary data. + * + */ + +/* Status of a connection */ +enum izip_status { IZIP_OPEN, IZIP_CLOSED }; + +/* Maximum transfer size per read operation */ +const unsigned int CHUNK = 128 * 1024; + +/* This class manages a compressed chunk of data preceeded by + * a length count. + * + * It can handle having multiple chunks of data in the buffer + * at any time. + */ +class CountedBuffer : public classbase +{ + std::string buffer; /* Current buffer contents */ + unsigned int amount_expected; /* Amount of data expected */ + public: + CountedBuffer() + { + amount_expected = 0; + } + + /** Adds arbitrary compressed data to the buffer. + * - Binsry safe, of course. + */ + void AddData(unsigned char* data, int data_length) + { + buffer.append((const char*)data, data_length); + this->NextFrameSize(); + } + + /** Works out the size of the next compressed frame + */ + void NextFrameSize() + { + if ((!amount_expected) && (buffer.length() >= 4)) + { + /* We have enough to read an int - + * Yes, this is safe, but its ugly. Give me + * a nicer way to read 4 bytes from a binary + * stream, and push them into a 32 bit int, + * and i'll consider replacing this. + */ + amount_expected = ntohl((buffer[3] << 24) | (buffer[2] << 16) | (buffer[1] << 8) | buffer[0]); + buffer = buffer.substr(4); + } + } + + /** Gets the next frame and returns its size, or returns + * zero if there isnt one available yet. + * A frame can contain multiple plaintext lines. + * - Binary safe. + */ + int GetFrame(unsigned char* frame, int maxsize) + { + if (amount_expected) + { + /* We know how much we're expecting... + * Do we have enough yet? + */ + if (buffer.length() >= amount_expected) + { + int j = 0; + for (unsigned int i = 0; i < amount_expected; i++, j++) + frame[i] = buffer[i]; + + buffer = buffer.substr(j); + amount_expected = 0; + NextFrameSize(); + return j; + } + } + /* Not enough for a frame yet, COME AGAIN! */ + return 0; + } +}; + +/** Represents an zipped connections extra data + */ +class izip_session : public classbase +{ + public: + z_stream c_stream; /* compression stream */ + z_stream d_stream; /* decompress stream */ + izip_status status; /* Connection status */ + int fd; /* File descriptor */ + CountedBuffer* inbuf; /* Holds input buffer */ + std::string outbuf; /* Holds output buffer */ +}; + +class ModuleZLib : public Module +{ + izip_session sessions[MAX_DESCRIPTORS]; + + /* Used for stats z extensions */ + float total_out_compressed; + float total_in_compressed; + float total_out_uncompressed; + float total_in_uncompressed; + + public: + + ModuleZLib(InspIRCd* Me) + : Module::Module(Me) + { + ServerInstance->PublishInterface("InspSocketHook", this); + + total_out_compressed = total_in_compressed = 0; + total_out_uncompressed = total_out_uncompressed = 0; + } + + virtual ~ModuleZLib() + { + ServerInstance->UnpublishInterface("InspSocketHook", this); + } + + virtual Version GetVersion() + { + return Version(1, 1, 0, 0, VF_VENDOR, API_VERSION); + } + + void Implements(char* List) + { + List[I_OnRawSocketConnect] = List[I_OnRawSocketAccept] = List[I_OnRawSocketClose] = List[I_OnRawSocketRead] = List[I_OnRawSocketWrite] = 1; + List[I_OnStats] = List[I_OnRequest] = 1; + } + + /* Handle InspSocketHook API requests */ + virtual char* OnRequest(Request* request) + { + ISHRequest* ISR = (ISHRequest*)request; + if (strcmp("IS_NAME", request->GetId()) == 0) + { + /* Return name */ + return "zip"; + } + else if (strcmp("IS_HOOK", request->GetId()) == 0) + { + /* Attach to an inspsocket */ + char* ret = "OK"; + try + { + ret = ServerInstance->Config->AddIOHook((Module*)this, (InspSocket*)ISR->Sock) ? (char*)"OK" : NULL; + } + catch (ModuleException& e) + { + return NULL; + } + return ret; + } + else if (strcmp("IS_UNHOOK", request->GetId()) == 0) + { + /* Detatch from an inspsocket */ + return ServerInstance->Config->DelIOHook((InspSocket*)ISR->Sock) ? (char*)"OK" : NULL; + } + else if (strcmp("IS_HSDONE", request->GetId()) == 0) + { + /* Check for completion of handshake + * (actually, this module doesnt handshake) + */ + return "OK"; + } + else if (strcmp("IS_ATTACH", request->GetId()) == 0) + { + /* Attach certificate data to the inspsocket + * (this module doesnt do that, either) + */ + return NULL; + } + return NULL; + } + + /* Handle stats z (misc stats) */ + virtual int OnStats(char symbol, userrec* user, string_list &results) + { + if (symbol == 'z') + { + std::string sn = ServerInstance->Config->ServerName; + + /* Yeah yeah, i know, floats are ew. + * We used them here because we'd be casting to float anyway to do this maths, + * and also only floating point numbers can deal with the pretty large numbers + * involved in the total throughput of a server over a large period of time. + * (we dont count 64 bit ints because not all systems have 64 bit ints, and floats + * can still hold more. + */ + float outbound_r = 100 - ((total_out_compressed / (total_out_uncompressed + 0.001)) * 100); + float inbound_r = 100 - ((total_in_compressed / (total_in_uncompressed + 0.001)) * 100); + + float total_compressed = total_in_compressed + total_out_compressed; + float total_uncompressed = total_in_uncompressed + total_out_uncompressed; + + float total_r = 100 - ((total_compressed / (total_uncompressed + 0.001)) * 100); + + char outbound_ratio[MAXBUF], inbound_ratio[MAXBUF], combined_ratio[MAXBUF]; + + sprintf(outbound_ratio, "%3.2f%%", outbound_r); + sprintf(inbound_ratio, "%3.2f%%", inbound_r); + sprintf(combined_ratio, "%3.2f%%", total_r); + + results.push_back(sn+" 304 "+user->nick+" :ZIPSTATS outbound_compressed = "+ConvToStr(total_out_compressed)); + results.push_back(sn+" 304 "+user->nick+" :ZIPSTATS inbound_compressed = "+ConvToStr(total_in_compressed)); + results.push_back(sn+" 304 "+user->nick+" :ZIPSTATS outbound_uncompressed = "+ConvToStr(total_out_uncompressed)); + results.push_back(sn+" 304 "+user->nick+" :ZIPSTATS inbound_uncompressed = "+ConvToStr(total_in_uncompressed)); + results.push_back(sn+" 304 "+user->nick+" :ZIPSTATS outbound_ratio = "+outbound_ratio); + results.push_back(sn+" 304 "+user->nick+" :ZIPSTATS inbound_ratio = "+inbound_ratio); + results.push_back(sn+" 304 "+user->nick+" :ZIPSTATS combined_ratio = "+combined_ratio); + return 0; + } + + return 0; + } + + virtual void OnRawSocketAccept(int fd, const std::string &ip, int localport) + { + izip_session* session = &sessions[fd]; + + /* allocate state and buffers */ + session->fd = fd; + session->status = IZIP_OPEN; + session->inbuf = new CountedBuffer(); + + session->c_stream.zalloc = (alloc_func)0; + session->c_stream.zfree = (free_func)0; + session->c_stream.opaque = (voidpf)0; + + session->d_stream.zalloc = (alloc_func)0; + session->d_stream.zfree = (free_func)0; + session->d_stream.opaque = (voidpf)0; + } + + virtual void OnRawSocketConnect(int fd) + { + /* Nothing special needs doing here compared to accept() */ + OnRawSocketAccept(fd, "", 0); + } + + virtual void OnRawSocketClose(int fd) + { + CloseSession(&sessions[fd]); + } + + virtual int OnRawSocketRead(int fd, char* buffer, unsigned int count, int &readresult) + { + /* Find the sockets session */ + izip_session* session = &sessions[fd]; + + if (session->status == IZIP_CLOSED) + return 0; + + unsigned char compr[CHUNK + 4]; + unsigned int offset = 0; + unsigned int total_size = 0; + + /* Read CHUNK bytes at a time to the buffer (usually 128k) */ + readresult = read(fd, compr, CHUNK); + + /* Did we get anything? */ + if (readresult > 0) + { + /* Add it to the frame queue */ + session->inbuf->AddData(compr, readresult); + total_in_compressed += readresult; + + /* Parse all completed frames */ + int size = 0; + while ((size = session->inbuf->GetFrame(compr, CHUNK)) != 0) + { + session->d_stream.next_in = (Bytef*)compr; + session->d_stream.avail_in = 0; + session->d_stream.next_out = (Bytef*)(buffer + offset); + + /* If we cant call this, well, we're boned. */ + if (inflateInit(&session->d_stream) != Z_OK) + return 0; + + while ((session->d_stream.total_out < count) && (session->d_stream.total_in < (unsigned int)size)) + { + session->d_stream.avail_in = session->d_stream.avail_out = 1; + if (inflate(&session->d_stream, Z_NO_FLUSH) == Z_STREAM_END) + break; + } + + /* Stick a fork in me, i'm done */ + inflateEnd(&session->d_stream); + + /* Update counters and offsets */ + total_size += session->d_stream.total_out; + total_in_uncompressed += session->d_stream.total_out; + offset += session->d_stream.total_out; + } + + /* Null-terminate the buffer -- this doesnt harm binary data */ + buffer[total_size] = 0; + + /* Set the read size to the correct total size */ + readresult = total_size; + + } + return (readresult > 0); + } + + virtual int OnRawSocketWrite(int fd, const char* buffer, int count) + { + izip_session* session = &sessions[fd]; + int ocount = count; + + if (!count) /* Nothing to do! */ + return 0; + + if(session->status != IZIP_OPEN) + { + /* Seriously, wtf? */ + CloseSession(session); + return 0; + } + + unsigned char compr[CHUNK + 4]; + + /* Gentlemen, start your engines! */ + if (deflateInit(&session->c_stream, Z_BEST_COMPRESSION) != Z_OK) + { + CloseSession(session); + return 0; + } + + /* Set buffer sizes (we reserve 4 bytes at the start of the + * buffer for the length counters) + */ + session->c_stream.next_in = (Bytef*)buffer; + session->c_stream.next_out = compr + 4; + + /* Compress the text */ + while ((session->c_stream.total_in < (unsigned int)count) && (session->c_stream.total_out < CHUNK)) + { + session->c_stream.avail_in = session->c_stream.avail_out = 1; + if (deflate(&session->c_stream, Z_NO_FLUSH) != Z_OK) + { + CloseSession(session); + return 0; + } + } + /* Finish the stream */ + for (session->c_stream.avail_out = 1; deflate(&session->c_stream, Z_FINISH) != Z_STREAM_END; session->c_stream.avail_out = 1); + deflateEnd(&session->c_stream); + + total_out_uncompressed += ocount; + total_out_compressed += session->c_stream.total_out; + + /** Assemble the frame length onto the frame, in network byte order */ + compr[0] = (session->c_stream.total_out >> 24); + compr[1] = (session->c_stream.total_out >> 16); + compr[2] = (session->c_stream.total_out >> 8); + compr[3] = (session->c_stream.total_out & 0xFF); + + /* Add compressed data plus leading length to the output buffer - + * Note, we may have incomplete half-sent frames in here. + */ + session->outbuf.append((const char*)compr, session->c_stream.total_out + 4); + + /* Lets see how much we can send out */ + int ret = write(fd, session->outbuf.data(), session->outbuf.length()); + + /* Check for errors, and advance the buffer if any was sent */ + if (ret > 0) + session->outbuf = session->outbuf.substr(ret); + else if (ret < 1) + { + if (ret == -1) + { + if (errno == EAGAIN) + return 0; + else + { + session->outbuf.clear(); + return 0; + } + } + else + { + session->outbuf.clear(); + return 0; + } + } + + /* ALL LIES the lot of it, we havent really written + * this amount, but the layer above doesnt need to know. + */ + return ocount; + } + + void CloseSession(izip_session* session) + { + if (session->status == IZIP_OPEN) + { + session->status = IZIP_CLOSED; + session->outbuf.clear(); + delete session->inbuf; + } + } + +}; + +MODULE_INIT(ModuleZLib); + diff --git a/src/modules/httpclient.h b/src/modules/httpclient.h index 109bfa666..c5e84261f 100644 --- a/src/modules/httpclient.h +++ b/src/modules/httpclient.h @@ -1 +1,127 @@ -/* +------------------------------------+
* | 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 "base.h"
#ifndef HTTPCLIENT_H__
#define HTTPCLIENT_H__
#include <string>
#include <map>
typedef std::map<std::string,std::string> HeaderMap;
const char* HTTP_CLIENT_RESPONSE = "HTTPCLIENT_RESPONSE";
const char* HTTP_CLIENT_REQUEST = "HTTPCLIENT_REQUEST";
/** This class represents an outgoing HTTP request
*/
class HTTPClientRequest : public Request
{
protected:
std::string url;
InspIRCd *Instance;
Module *src;
HeaderMap Headers;
public:
HTTPClientRequest(InspIRCd *Instance, Module *src, Module* target, const std::string &url)
: Request(src, target, HTTP_CLIENT_REQUEST), url(url), Instance(Instance), src(src)
{
Headers["User-Agent"] = "InspIRCd (m_http_client.so)";
Headers["Connection"] = "Close";
Headers["Accept"] = "*/*";
}
HTTPClientRequest() : Request(NULL, NULL, HTTP_CLIENT_REQUEST)
{
}
const std::string &GetURL()
{
return url;
}
void AddHeader(std::string &header, std::string &data)
{
Headers[header] = data;
}
void DeleteHeader(std::string &header)
{
Headers.erase(header);
}
HeaderMap GetHeaders()
{
return Headers;
}
};
class HTTPClientResponse : public Request
{
protected:
friend class HTTPSocket;
std::string url;
std::string data;
int response;
std::string responsestr;
HeaderMap Headers;
public:
HTTPClientResponse(Module* src, Module* target, std::string &url, int response, std::string responsestr)
: Request(src, target, HTTP_CLIENT_RESPONSE), url(url), response(response), responsestr(responsestr)
{
}
HTTPClientResponse() : Request(NULL, NULL, HTTP_CLIENT_RESPONSE)
{
}
void SetData(const std::string &ndata)
{
data = ndata;
}
void AddHeader(const std::string &header, const std::string &data)
{
Headers[header] = data;
}
const std::string &GetURL()
{
return url;
}
const std::string &GetData()
{
return data;
}
int GetResponse(std::string &str)
{
str = responsestr;
return response;
}
std::string GetHeader(const std::string &header)
{
HeaderMap::iterator i = Headers.find(header);
if (i != Headers.end())
return i->second;
else
return "";
}
};
#endif
\ No newline at end of file +/* +------------------------------------+ + * | 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 "base.h" + +#ifndef HTTPCLIENT_H__ +#define HTTPCLIENT_H__ + +#include <string> +#include <map> + +typedef std::map<std::string,std::string> HeaderMap; + +const char* HTTP_CLIENT_RESPONSE = "HTTPCLIENT_RESPONSE"; +const char* HTTP_CLIENT_REQUEST = "HTTPCLIENT_REQUEST"; + +/** This class represents an outgoing HTTP request + */ +class HTTPClientRequest : public Request +{ + protected: + std::string url; + InspIRCd *Instance; + Module *src; + HeaderMap Headers; + public: + HTTPClientRequest(InspIRCd *Instance, Module *src, Module* target, const std::string &url) + : Request(src, target, HTTP_CLIENT_REQUEST), url(url), Instance(Instance), src(src) + { + Headers["User-Agent"] = "InspIRCd (m_http_client.so)"; + Headers["Connection"] = "Close"; + Headers["Accept"] = "*/*"; + } + + HTTPClientRequest() : Request(NULL, NULL, HTTP_CLIENT_REQUEST) + { + } + + const std::string &GetURL() + { + return url; + } + + void AddHeader(std::string &header, std::string &data) + { + Headers[header] = data; + } + + void DeleteHeader(std::string &header) + { + Headers.erase(header); + } + + HeaderMap GetHeaders() + { + return Headers; + } +}; + +class HTTPClientResponse : public Request +{ + protected: + friend class HTTPSocket; + + std::string url; + std::string data; + int response; + std::string responsestr; + HeaderMap Headers; + public: + HTTPClientResponse(Module* src, Module* target, std::string &url, int response, std::string responsestr) + : Request(src, target, HTTP_CLIENT_RESPONSE), url(url), response(response), responsestr(responsestr) + { + } + + HTTPClientResponse() : Request(NULL, NULL, HTTP_CLIENT_RESPONSE) + { + } + + void SetData(const std::string &ndata) + { + data = ndata; + } + + void AddHeader(const std::string &header, const std::string &data) + { + Headers[header] = data; + } + + const std::string &GetURL() + { + return url; + } + + const std::string &GetData() + { + return data; + } + + int GetResponse(std::string &str) + { + str = responsestr; + return response; + } + + std::string GetHeader(const std::string &header) + { + HeaderMap::iterator i = Headers.find(header); + + if (i != Headers.end()) + return i->second; + else + return ""; + } +}; + +#endif diff --git a/src/modules/httpd.h b/src/modules/httpd.h index 32bac757f..a8b0bafcd 100644 --- a/src/modules/httpd.h +++ b/src/modules/httpd.h @@ -1 +1,166 @@ -/* +------------------------------------+
* | 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 "base.h"
#ifndef __HTTPD_H__
#define __HTTPD_H__
#include <string>
#include <sstream>
/** This class represents a HTTP request.
* It will be sent to all modules as the data section of
* an Event.
*/
class HTTPRequest : public classbase
{
protected:
std::string type;
std::string document;
std::string ipaddr;
std::string postdata;
std::stringstream* headers;
public:
/** A socket pointer, which you must return in your HTTPDocument class
* if you reply to this request.
*/
void* sock;
/** Initialize HTTPRequest.
* This constructor is called by m_httpd.so to initialize the class.
* @param request_type The request type, e.g. GET, POST, HEAD
* @param uri The URI, e.g. /page
* @param hdr The headers sent with the request
* @param opaque An opaque pointer used internally by m_httpd, which you must pass back to the module in your reply.
* @param ip The IP address making the web request.
* @param pdata The post data (content after headers) received with the request, up to Content-Length in size
*/
HTTPRequest(const std::string &request_type, const std::string &uri, std::stringstream* hdr, void* opaque, const std::string &ip, const std::string &pdata)
: type(request_type), document(uri), ipaddr(ip), postdata(pdata), headers(hdr), sock(opaque)
{
}
/** Get headers.
* All the headers for the web request are returned, as a pointer to a stringstream.
* @return The header information
*/
std::stringstream* GetHeaders()
{
return headers;
}
/** Get the post data (request content).
* All post data will be returned, including carriage returns and linefeeds.
* @return The postdata
*/
std::string& GetPostData()
{
return postdata;
}
/** Get the request type.
* Any request type can be intercepted, even ones which are invalid in the HTTP/1.1 spec.
* @return The request type, e.g. GET, POST, HEAD
*/
std::string& GetType()
{
return type;
}
/** Get URI.
* The URI string (URL minus hostname and scheme) will be provided by this function.
* @return The URI being requested
*/
std::string& GetURI()
{
return document;
}
/** Get IP address of requester.
* The requesting system's ip address will be returned.
* @return The IP address as a string
*/
std::string& GetIP()
{
return ipaddr;
}
};
/** You must return a HTTPDocument to the httpd module by using the Request class.
* When you initialize this class you may initialize it with all components required to
* form a valid HTTP response, including document data, headers, and a response code.
*/
class HTTPDocument : public classbase
{
protected:
std::stringstream* document;
int responsecode;
std::string extraheaders;
public:
/** The socket pointer from an earlier HTTPRequest
*/
void* sock;
/** Initialize a HTTPRequest ready for sending to m_httpd.so.
* @param opaque The socket pointer you obtained from the HTTPRequest at an earlier time
* @param doc A stringstream containing the document body
* @param response A valid HTTP/1.0 or HTTP/1.1 response code. The response text will be determined for you
* based upon the response code.
* @param extra Any extra headers to include with the defaults, seperated by carriage return and linefeed.
*/
HTTPDocument(void* opaque, std::stringstream* doc, int response, const std::string &extra) : document(doc), responsecode(response), extraheaders(extra), sock(opaque)
{
}
/** Get the document text.
* @return The document text
*/
std::stringstream* GetDocument()
{
return this->document;
}
/** Get the document size.
* @return the size of the document text in bytes
*/
unsigned long GetDocumentSize()
{
return this->document->str().length();
}
/** Get the response code.
* @return The response code
*/
int GetResponseCode()
{
return this->responsecode;
}
/** Get the headers.
* @return The header text, headers seperated by carriage return and linefeed.
*/
std::string& GetExtraHeaders()
{
return this->extraheaders;
}
};
#endif
\ No newline at end of file +/* +------------------------------------+ + * | 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 "base.h" + +#ifndef __HTTPD_H__ +#define __HTTPD_H__ + +#include <string> +#include <sstream> + +/** This class represents a HTTP request. + * It will be sent to all modules as the data section of + * an Event. + */ +class HTTPRequest : public classbase +{ + protected: + + std::string type; + std::string document; + std::string ipaddr; + std::string postdata; + std::stringstream* headers; + + public: + + /** A socket pointer, which you must return in your HTTPDocument class + * if you reply to this request. + */ + void* sock; + + /** Initialize HTTPRequest. + * This constructor is called by m_httpd.so to initialize the class. + * @param request_type The request type, e.g. GET, POST, HEAD + * @param uri The URI, e.g. /page + * @param hdr The headers sent with the request + * @param opaque An opaque pointer used internally by m_httpd, which you must pass back to the module in your reply. + * @param ip The IP address making the web request. + * @param pdata The post data (content after headers) received with the request, up to Content-Length in size + */ + HTTPRequest(const std::string &request_type, const std::string &uri, std::stringstream* hdr, void* opaque, const std::string &ip, const std::string &pdata) + : type(request_type), document(uri), ipaddr(ip), postdata(pdata), headers(hdr), sock(opaque) + { + } + + /** Get headers. + * All the headers for the web request are returned, as a pointer to a stringstream. + * @return The header information + */ + std::stringstream* GetHeaders() + { + return headers; + } + + /** Get the post data (request content). + * All post data will be returned, including carriage returns and linefeeds. + * @return The postdata + */ + std::string& GetPostData() + { + return postdata; + } + + /** Get the request type. + * Any request type can be intercepted, even ones which are invalid in the HTTP/1.1 spec. + * @return The request type, e.g. GET, POST, HEAD + */ + std::string& GetType() + { + return type; + } + + /** Get URI. + * The URI string (URL minus hostname and scheme) will be provided by this function. + * @return The URI being requested + */ + std::string& GetURI() + { + return document; + } + + /** Get IP address of requester. + * The requesting system's ip address will be returned. + * @return The IP address as a string + */ + std::string& GetIP() + { + return ipaddr; + } +}; + +/** You must return a HTTPDocument to the httpd module by using the Request class. + * When you initialize this class you may initialize it with all components required to + * form a valid HTTP response, including document data, headers, and a response code. + */ +class HTTPDocument : public classbase +{ + protected: + + std::stringstream* document; + int responsecode; + std::string extraheaders; + + public: + + /** The socket pointer from an earlier HTTPRequest + */ + void* sock; + + /** Initialize a HTTPRequest ready for sending to m_httpd.so. + * @param opaque The socket pointer you obtained from the HTTPRequest at an earlier time + * @param doc A stringstream containing the document body + * @param response A valid HTTP/1.0 or HTTP/1.1 response code. The response text will be determined for you + * based upon the response code. + * @param extra Any extra headers to include with the defaults, seperated by carriage return and linefeed. + */ + HTTPDocument(void* opaque, std::stringstream* doc, int response, const std::string &extra) : document(doc), responsecode(response), extraheaders(extra), sock(opaque) + { + } + + /** Get the document text. + * @return The document text + */ + std::stringstream* GetDocument() + { + return this->document; + } + + /** Get the document size. + * @return the size of the document text in bytes + */ + unsigned long GetDocumentSize() + { + return this->document->str().length(); + } + + /** Get the response code. + * @return The response code + */ + int GetResponseCode() + { + return this->responsecode; + } + + /** Get the headers. + * @return The header text, headers seperated by carriage return and linefeed. + */ + std::string& GetExtraHeaders() + { + return this->extraheaders; + } +}; + +#endif + diff --git a/src/modules/m_alias.cpp b/src/modules/m_alias.cpp index 039aeed92..94c64b405 100644 --- a/src/modules/m_alias.cpp +++ b/src/modules/m_alias.cpp @@ -1 +1,272 @@ -/* +------------------------------------+
* | 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 "users.h"
#include "channels.h"
#include "modules.h"
#include "wildcard.h"
/* $ModDesc: Provides aliases of commands. */
/** An alias definition
*/
class Alias : public classbase
{
public:
/** The text of the alias command */
irc::string text;
/** Text to replace with */
std::string replace_with;
/** Nickname required to perform alias */
std::string requires;
/** Alias requires ulined server */
bool uline;
/** Requires oper? */
bool operonly;
/* is case sensitive params */
bool case_sensitive;
/** Format that must be matched for use */
std::string format;
};
class ModuleAlias : public Module
{
private:
/** We cant use a map, there may be multiple aliases with the same name */
std::vector<Alias> Aliases;
std::map<std::string, int> AliasMap;
std::vector<std::string> pars;
virtual void ReadAliases()
{
ConfigReader MyConf(ServerInstance);
Aliases.clear();
AliasMap.clear();
for (int i = 0; i < MyConf.Enumerate("alias"); i++)
{
Alias a;
std::string txt;
txt = MyConf.ReadValue("alias", "text", i);
a.text = txt.c_str();
a.replace_with = MyConf.ReadValue("alias", "replace", i, true);
a.requires = MyConf.ReadValue("alias", "requires", i);
a.uline = MyConf.ReadFlag("alias", "uline", i);
a.operonly = MyConf.ReadFlag("alias", "operonly", i);
a.format = MyConf.ReadValue("alias", "format", i);
a.case_sensitive = MyConf.ReadFlag("alias", "matchcase", i);
Aliases.push_back(a);
AliasMap[txt] = 1;
}
}
public:
ModuleAlias(InspIRCd* Me)
: Module(Me)
{
ReadAliases();
pars.resize(127);
}
void Implements(char* List)
{
List[I_OnPreCommand] = List[I_OnRehash] = 1;
}
virtual ~ModuleAlias()
{
}
virtual Version GetVersion()
{
return Version(1,1,0,1,VF_VENDOR,API_VERSION);
}
std::string GetVar(std::string varname, const std::string &original_line)
{
irc::spacesepstream ss(original_line);
varname.erase(varname.begin());
int index = *(varname.begin()) - 48;
varname.erase(varname.begin());
bool everything_after = (varname == "-");
std::string word;
for (int j = 0; j < index; j++)
word = ss.GetToken();
if (everything_after)
{
std::string more = "*";
while ((more = ss.GetToken()) != "")
{
word.append(" ");
word.append(more);
}
}
return word;
}
void SearchAndReplace(std::string& newline, const std::string &find, const std::string &replace)
{
std::string::size_type x = newline.find(find);
while (x != std::string::npos)
{
newline.erase(x, find.length());
newline.insert(x, replace);
x = newline.find(find);
}
}
virtual int OnPreCommand(const std::string &command, const char** parameters, int pcnt, userrec *user, bool validated, const std::string &original_line)
{
userrec *u = NULL;
/* If theyre not registered yet, we dont want
* to know.
*/
if (user->registered != REG_ALL)
return 0;
/* We dont have any commands looking like this, dont bother with the loop */
if (AliasMap.find(command) == AliasMap.end())
return 0;
irc::string c = command.c_str();
/* The parameters for the command in their original form, with the command stripped off */
std::string compare = original_line.substr(command.length());
while (*(compare.c_str()) == ' ')
compare.erase(compare.begin());
std::string safe(original_line);
/* Escape out any $ symbols in the user provided text */
SearchAndReplace(safe, "$", "\r");
for (unsigned int i = 0; i < Aliases.size(); i++)
{
if (Aliases[i].text == c)
{
/* Does it match the pattern? */
if (!Aliases[i].format.empty())
{
if (!match(Aliases[i].case_sensitive, compare.c_str(), Aliases[i].format.c_str()))
continue;
}
if ((Aliases[i].operonly) && (!IS_OPER(user)))
return 0;
if (!Aliases[i].requires.empty())
{
u = ServerInstance->FindNick(Aliases[i].requires);
if (!u)
{
user->WriteServ("401 "+std::string(user->nick)+" "+Aliases[i].requires+" :is currently unavailable. Please try again later.");
return 1;
}
}
if ((u != NULL) && (!Aliases[i].requires.empty()) && (Aliases[i].uline))
{
if (!ServerInstance->ULine(u->server))
{
ServerInstance->WriteOpers("*** NOTICE -- Service "+Aliases[i].requires+" required by alias "+std::string(Aliases[i].text.c_str())+" is not on a u-lined server, possibly underhanded antics detected!");
user->WriteServ("401 "+std::string(user->nick)+" "+Aliases[i].requires+" :is an imposter! Please inform an IRC operator as soon as possible.");
return 1;
}
}
/* Now, search and replace in a copy of the original_line, replacing $1 through $9 and $1- etc */
std::string::size_type crlf = Aliases[i].replace_with.find('\n');
if (crlf == std::string::npos)
{
DoCommand(Aliases[i].replace_with, user, safe);
return 1;
}
else
{
irc::sepstream commands(Aliases[i].replace_with, '\n');
std::string command = "*";
while ((command = commands.GetToken()) != "")
{
DoCommand(command, user, safe);
}
return 1;
}
}
}
return 0;
}
void DoCommand(std::string newline, userrec* user, const std::string &original_line)
{
for (int v = 1; v < 10; v++)
{
std::string var = "$";
var.append(ConvToStr(v));
var.append("-");
std::string::size_type x = newline.find(var);
while (x != std::string::npos)
{
newline.erase(x, var.length());
newline.insert(x, GetVar(var, original_line));
x = newline.find(var);
}
var = "$";
var.append(ConvToStr(v));
x = newline.find(var);
while (x != std::string::npos)
{
newline.erase(x, var.length());
newline.insert(x, GetVar(var, original_line));
x = newline.find(var);
}
}
/* Special variables */
SearchAndReplace(newline, "$nick", user->nick);
SearchAndReplace(newline, "$ident", user->ident);
SearchAndReplace(newline, "$host", user->host);
SearchAndReplace(newline, "$vhost", user->dhost);
/* Unescape any variable names in the user text before sending */
SearchAndReplace(newline, "\r", "$");
irc::tokenstream ss(newline);
const char* parv[127];
int x = 0;
while (ss.GetToken(pars[x]))
{
parv[x] = pars[x].c_str();
x++;
}
ServerInstance->Parser->CallHandler(parv[0], &parv[1], x-1, user);
}
virtual void OnRehash(userrec* user, const std::string ¶meter)
{
ReadAliases();
}
};
MODULE_INIT(ModuleAlias)
\ No newline at end of file +/* +------------------------------------+ + * | 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 "users.h" +#include "channels.h" +#include "modules.h" +#include "wildcard.h" + +/* $ModDesc: Provides aliases of commands. */ + +/** An alias definition + */ +class Alias : public classbase +{ + public: + /** The text of the alias command */ + irc::string text; + /** Text to replace with */ + std::string replace_with; + /** Nickname required to perform alias */ + std::string requires; + /** Alias requires ulined server */ + bool uline; + /** Requires oper? */ + bool operonly; + /* is case sensitive params */ + bool case_sensitive; + /** Format that must be matched for use */ + std::string format; +}; + +class ModuleAlias : public Module +{ + private: + /** We cant use a map, there may be multiple aliases with the same name */ + std::vector<Alias> Aliases; + std::map<std::string, int> AliasMap; + std::vector<std::string> pars; + + virtual void ReadAliases() + { + ConfigReader MyConf(ServerInstance); + + Aliases.clear(); + AliasMap.clear(); + for (int i = 0; i < MyConf.Enumerate("alias"); i++) + { + Alias a; + std::string txt; + txt = MyConf.ReadValue("alias", "text", i); + a.text = txt.c_str(); + a.replace_with = MyConf.ReadValue("alias", "replace", i, true); + a.requires = MyConf.ReadValue("alias", "requires", i); + a.uline = MyConf.ReadFlag("alias", "uline", i); + a.operonly = MyConf.ReadFlag("alias", "operonly", i); + a.format = MyConf.ReadValue("alias", "format", i); + a.case_sensitive = MyConf.ReadFlag("alias", "matchcase", i); + Aliases.push_back(a); + AliasMap[txt] = 1; + } + } + + public: + + ModuleAlias(InspIRCd* Me) + : Module(Me) + { + ReadAliases(); + pars.resize(127); + } + + void Implements(char* List) + { + List[I_OnPreCommand] = List[I_OnRehash] = 1; + } + + virtual ~ModuleAlias() + { + } + + virtual Version GetVersion() + { + return Version(1,1,0,1,VF_VENDOR,API_VERSION); + } + + std::string GetVar(std::string varname, const std::string &original_line) + { + irc::spacesepstream ss(original_line); + varname.erase(varname.begin()); + int index = *(varname.begin()) - 48; + varname.erase(varname.begin()); + bool everything_after = (varname == "-"); + std::string word; + + for (int j = 0; j < index; j++) + word = ss.GetToken(); + + if (everything_after) + { + std::string more = "*"; + while ((more = ss.GetToken()) != "") + { + word.append(" "); + word.append(more); + } + } + + return word; + } + + void SearchAndReplace(std::string& newline, const std::string &find, const std::string &replace) + { + std::string::size_type x = newline.find(find); + while (x != std::string::npos) + { + newline.erase(x, find.length()); + newline.insert(x, replace); + x = newline.find(find); + } + } + + virtual int OnPreCommand(const std::string &command, const char** parameters, int pcnt, userrec *user, bool validated, const std::string &original_line) + { + userrec *u = NULL; + + /* If theyre not registered yet, we dont want + * to know. + */ + if (user->registered != REG_ALL) + return 0; + + /* We dont have any commands looking like this, dont bother with the loop */ + if (AliasMap.find(command) == AliasMap.end()) + return 0; + + irc::string c = command.c_str(); + /* The parameters for the command in their original form, with the command stripped off */ + std::string compare = original_line.substr(command.length()); + while (*(compare.c_str()) == ' ') + compare.erase(compare.begin()); + + std::string safe(original_line); + + /* Escape out any $ symbols in the user provided text */ + + SearchAndReplace(safe, "$", "\r"); + + for (unsigned int i = 0; i < Aliases.size(); i++) + { + if (Aliases[i].text == c) + { + /* Does it match the pattern? */ + if (!Aliases[i].format.empty()) + { + if (!match(Aliases[i].case_sensitive, compare.c_str(), Aliases[i].format.c_str())) + continue; + } + + if ((Aliases[i].operonly) && (!IS_OPER(user))) + return 0; + + if (!Aliases[i].requires.empty()) + { + u = ServerInstance->FindNick(Aliases[i].requires); + if (!u) + { + user->WriteServ("401 "+std::string(user->nick)+" "+Aliases[i].requires+" :is currently unavailable. Please try again later."); + return 1; + } + } + if ((u != NULL) && (!Aliases[i].requires.empty()) && (Aliases[i].uline)) + { + if (!ServerInstance->ULine(u->server)) + { + ServerInstance->WriteOpers("*** NOTICE -- Service "+Aliases[i].requires+" required by alias "+std::string(Aliases[i].text.c_str())+" is not on a u-lined server, possibly underhanded antics detected!"); + user->WriteServ("401 "+std::string(user->nick)+" "+Aliases[i].requires+" :is an imposter! Please inform an IRC operator as soon as possible."); + return 1; + } + } + + /* Now, search and replace in a copy of the original_line, replacing $1 through $9 and $1- etc */ + + std::string::size_type crlf = Aliases[i].replace_with.find('\n'); + + if (crlf == std::string::npos) + { + DoCommand(Aliases[i].replace_with, user, safe); + return 1; + } + else + { + irc::sepstream commands(Aliases[i].replace_with, '\n'); + std::string command = "*"; + while ((command = commands.GetToken()) != "") + { + DoCommand(command, user, safe); + } + return 1; + } + } + } + return 0; + } + + void DoCommand(std::string newline, userrec* user, const std::string &original_line) + { + for (int v = 1; v < 10; v++) + { + std::string var = "$"; + var.append(ConvToStr(v)); + var.append("-"); + std::string::size_type x = newline.find(var); + + while (x != std::string::npos) + { + newline.erase(x, var.length()); + newline.insert(x, GetVar(var, original_line)); + x = newline.find(var); + } + + var = "$"; + var.append(ConvToStr(v)); + x = newline.find(var); + + while (x != std::string::npos) + { + newline.erase(x, var.length()); + newline.insert(x, GetVar(var, original_line)); + x = newline.find(var); + } + } + + /* Special variables */ + SearchAndReplace(newline, "$nick", user->nick); + SearchAndReplace(newline, "$ident", user->ident); + SearchAndReplace(newline, "$host", user->host); + SearchAndReplace(newline, "$vhost", user->dhost); + + /* Unescape any variable names in the user text before sending */ + SearchAndReplace(newline, "\r", "$"); + + irc::tokenstream ss(newline); + const char* parv[127]; + int x = 0; + + while (ss.GetToken(pars[x])) + { + parv[x] = pars[x].c_str(); + x++; + } + + ServerInstance->Parser->CallHandler(parv[0], &parv[1], x-1, user); + } + + virtual void OnRehash(userrec* user, const std::string ¶meter) + { + ReadAliases(); + } +}; + +MODULE_INIT(ModuleAlias) diff --git a/src/modules/m_alltime.cpp b/src/modules/m_alltime.cpp index 6a3f27ba8..97ab6a3fe 100644 --- a/src/modules/m_alltime.cpp +++ b/src/modules/m_alltime.cpp @@ -1 +1,83 @@ -/* +------------------------------------+
* | 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 "modules.h"
/* $ModDesc: Display timestamps from all servers connected to the network */
class cmd_alltime : public command_t
{
public:
cmd_alltime(InspIRCd *Instance) : command_t(Instance, "ALLTIME", 'o', 0)
{
this->source = "m_alltime.so";
syntax.clear();
}
CmdResult Handle(const char **parameters, int pcnt, userrec *user)
{
char fmtdate[64];
char fmtdate2[64];
time_t now = ServerInstance->Time(false);
strftime(fmtdate, sizeof(fmtdate), "%F %T", gmtime(&now));
now = ServerInstance->Time(true);
strftime(fmtdate2, sizeof(fmtdate2), "%F %T", gmtime(&now));
int delta = ServerInstance->GetTimeDelta();
string msg = ":" + string(ServerInstance->Config->ServerName) + " NOTICE " + user->nick + " :System time for " +
ServerInstance->Config->ServerName + " is: " + fmtdate + " (delta " + ConvToStr(delta) + " seconds): Time with delta: "+ fmtdate2;
if (IS_LOCAL(user))
{
user->Write(msg);
}
else
{
deque<string> params;
params.push_back(user->nick);
params.push_back(msg);
Event ev((char *) ¶ms, NULL, "send_push");
ev.Send(ServerInstance);
}
/* we want this routed out! */
return CMD_SUCCESS;
}
};
class Modulealltime : public Module
{
cmd_alltime *mycommand;
public:
Modulealltime(InspIRCd *Me)
: Module(Me)
{
mycommand = new cmd_alltime(ServerInstance);
ServerInstance->AddCommand(mycommand);
}
virtual ~Modulealltime()
{
}
virtual Version GetVersion()
{
return Version(1, 0, 0, 0, VF_COMMON | VF_VENDOR, API_VERSION);
}
};
MODULE_INIT(Modulealltime)
\ No newline at end of file +/* +------------------------------------+ + * | 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 "modules.h" + +/* $ModDesc: Display timestamps from all servers connected to the network */ + +class cmd_alltime : public command_t +{ + public: + cmd_alltime(InspIRCd *Instance) : command_t(Instance, "ALLTIME", 'o', 0) + { + this->source = "m_alltime.so"; + syntax.clear(); + } + + CmdResult Handle(const char **parameters, int pcnt, userrec *user) + { + char fmtdate[64]; + char fmtdate2[64]; + time_t now = ServerInstance->Time(false); + strftime(fmtdate, sizeof(fmtdate), "%F %T", gmtime(&now)); + now = ServerInstance->Time(true); + strftime(fmtdate2, sizeof(fmtdate2), "%F %T", gmtime(&now)); + + int delta = ServerInstance->GetTimeDelta(); + + string msg = ":" + string(ServerInstance->Config->ServerName) + " NOTICE " + user->nick + " :System time for " + + ServerInstance->Config->ServerName + " is: " + fmtdate + " (delta " + ConvToStr(delta) + " seconds): Time with delta: "+ fmtdate2; + + if (IS_LOCAL(user)) + { + user->Write(msg); + } + else + { + deque<string> params; + params.push_back(user->nick); + params.push_back(msg); + Event ev((char *) ¶ms, NULL, "send_push"); + ev.Send(ServerInstance); + } + + /* we want this routed out! */ + return CMD_SUCCESS; + } +}; + + +class Modulealltime : public Module +{ + cmd_alltime *mycommand; + public: + Modulealltime(InspIRCd *Me) + : Module(Me) + { + mycommand = new cmd_alltime(ServerInstance); + ServerInstance->AddCommand(mycommand); + } + + virtual ~Modulealltime() + { + } + + virtual Version GetVersion() + { + return Version(1, 0, 0, 0, VF_COMMON | VF_VENDOR, API_VERSION); + } + +}; + +MODULE_INIT(Modulealltime) diff --git a/src/modules/m_antibear.cpp b/src/modules/m_antibear.cpp index d95c70282..2718cbb4c 100644 --- a/src/modules/m_antibear.cpp +++ b/src/modules/m_antibear.cpp @@ -1 +1,78 @@ -/* +------------------------------------+
* | 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 "users.h"
#include "channels.h"
#include "modules.h"
#include "xline.h"
/* $ModDesc: Sends a numeric on connect which cripples a common type of trojan/spambot */
class ModuleAntiBear : public Module
{
private:
public:
ModuleAntiBear(InspIRCd* Me) : Module(Me)
{
}
virtual ~ModuleAntiBear()
{
}
virtual Version GetVersion()
{
return Version(1,1,0,0,VF_VENDOR,API_VERSION);
}
void Implements(char* List)
{
List[I_OnUserRegister] = List[I_OnPreCommand] = 1;
}
virtual int OnPreCommand(const std::string &command, const char** parameters, int pcnt, userrec *user, bool validated, const std::string &original_line)
{
if (command == "NOTICE" && !validated && pcnt > 1 && user->GetExt("antibear_timewait"))
{
if (!strncmp(parameters[1], "\1TIME Mon May 01 18:54:20 2006", 30))
{
if (ServerInstance->XLines->add_zline(86400, ServerInstance->Config->ServerName, "Unless you're stuck in a time warp, you appear to be a bear bot!", user->MakeHostIP()))
{
ServerInstance->XLines->apply_lines(APPLY_ZLINES);
FOREACH_MOD(I_OnAddGLine,OnAddZLine(86400, NULL, "Unless you're stuck in a time warp, you appear to be a bear bot!", user->MakeHostIP()));
return 1;
}
}
user->Shrink("antibear_timewait");
// Block the command, so the user doesn't receive a no such nick notice
return 1;
}
return 0;
}
virtual int OnUserRegister(userrec* user)
{
user->WriteServ("439 %s :This server has anti-spambot mechanisms enabled.", user->nick);
user->WriteServ("931 %s :Malicious bots, spammers, and other automated systems of dubious origin are NOT welcome here.", user->nick);
user->WriteServ("PRIVMSG %s :\1TIME\1", user->nick);
user->Extend("antibear_timewait");
return 0;
}
};
MODULE_INIT(ModuleAntiBear)
\ No newline at end of file +/* +------------------------------------+ + * | 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 "users.h" +#include "channels.h" +#include "modules.h" +#include "xline.h" + +/* $ModDesc: Sends a numeric on connect which cripples a common type of trojan/spambot */ + +class ModuleAntiBear : public Module +{ + private: + + public: + ModuleAntiBear(InspIRCd* Me) : Module(Me) + { + + } + + virtual ~ModuleAntiBear() + { + } + + virtual Version GetVersion() + { + return Version(1,1,0,0,VF_VENDOR,API_VERSION); + } + + void Implements(char* List) + { + List[I_OnUserRegister] = List[I_OnPreCommand] = 1; + } + + virtual int OnPreCommand(const std::string &command, const char** parameters, int pcnt, userrec *user, bool validated, const std::string &original_line) + { + if (command == "NOTICE" && !validated && pcnt > 1 && user->GetExt("antibear_timewait")) + { + if (!strncmp(parameters[1], "\1TIME Mon May 01 18:54:20 2006", 30)) + { + if (ServerInstance->XLines->add_zline(86400, ServerInstance->Config->ServerName, "Unless you're stuck in a time warp, you appear to be a bear bot!", user->MakeHostIP())) + { + ServerInstance->XLines->apply_lines(APPLY_ZLINES); + FOREACH_MOD(I_OnAddGLine,OnAddZLine(86400, NULL, "Unless you're stuck in a time warp, you appear to be a bear bot!", user->MakeHostIP())); + return 1; + } + } + + user->Shrink("antibear_timewait"); + // Block the command, so the user doesn't receive a no such nick notice + return 1; + } + + return 0; + } + + virtual int OnUserRegister(userrec* user) + { + user->WriteServ("439 %s :This server has anti-spambot mechanisms enabled.", user->nick); + user->WriteServ("931 %s :Malicious bots, spammers, and other automated systems of dubious origin are NOT welcome here.", user->nick); + user->WriteServ("PRIVMSG %s :\1TIME\1", user->nick); + user->Extend("antibear_timewait"); + return 0; + } +}; + +MODULE_INIT(ModuleAntiBear) diff --git a/src/modules/m_antibottler.cpp b/src/modules/m_antibottler.cpp index 907cfb39d..3aa5592cc 100644 --- a/src/modules/m_antibottler.cpp +++ b/src/modules/m_antibottler.cpp @@ -1 +1,99 @@ -/* +------------------------------------+
* | 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 "users.h"
#include "channels.h"
#include "modules.h"
/* $ModDesc: Changes the ident of connecting bottler clients to 'bottler' */
class ModuleAntiBottler : public Module
{
public:
ModuleAntiBottler(InspIRCd* Me)
: Module(Me)
{
}
void Implements(char* List)
{
List[I_OnPreCommand] = 1;
}
virtual ~ModuleAntiBottler()
{
}
virtual Version GetVersion()
{
return Version(1,1,0,1,VF_VENDOR,API_VERSION);
}
virtual int OnPreCommand(const std::string &command, const char** parameters, int pcnt, userrec *user, bool validated, const std::string &original_line)
{
char data[MAXBUF];
strlcpy(data,original_line.c_str(),MAXBUF);
bool not_bottler = false;
if (!strncmp(data,"user ",5))
{
for (char* j = data; *j; j++)
{
if (*j == ':')
break;
if (*j == '"')
{
not_bottler = true;
}
}
// Bug Fix (#14) -- FCS
if (!(data) || !(*data))
return 0;
strtok(data," ");
char *ident = strtok(NULL," ");
char *local = strtok(NULL," ");
char *remote = strtok(NULL," :");
char *gecos = strtok(NULL,"\r\n");
if (!ident || !local || !remote || !gecos)
return 0;
for (char* j = remote; *j; j++)
{
if (((*j < '0') || (*j > '9')) && (*j != '.'))
{
not_bottler = true;
}
}
if (!not_bottler)
{
std::string strgecos = std::string(gecos) + "[Possible bottler, ident: " + std::string(ident) + "]";
const char* modified[4];
modified[0] = "bottler";
modified[1] = local;
modified[2] = remote;
modified[3] = strgecos.c_str();
ServerInstance->Parser->CallHandler("USER", modified, 4, user);
return 1;
}
}
return 0;
}
};
MODULE_INIT(ModuleAntiBottler)
\ No newline at end of file +/* +------------------------------------+ + * | 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 "users.h" +#include "channels.h" +#include "modules.h" + +/* $ModDesc: Changes the ident of connecting bottler clients to 'bottler' */ + +class ModuleAntiBottler : public Module +{ + public: + ModuleAntiBottler(InspIRCd* Me) + : Module(Me) + { + + } + + void Implements(char* List) + { + List[I_OnPreCommand] = 1; + } + + + virtual ~ModuleAntiBottler() + { + } + + virtual Version GetVersion() + { + return Version(1,1,0,1,VF_VENDOR,API_VERSION); + } + + virtual int OnPreCommand(const std::string &command, const char** parameters, int pcnt, userrec *user, bool validated, const std::string &original_line) + { + char data[MAXBUF]; + strlcpy(data,original_line.c_str(),MAXBUF); + bool not_bottler = false; + if (!strncmp(data,"user ",5)) + { + for (char* j = data; *j; j++) + { + if (*j == ':') + break; + + if (*j == '"') + { + not_bottler = true; + } + } + // Bug Fix (#14) -- FCS + if (!(data) || !(*data)) + return 0; + + strtok(data," "); + char *ident = strtok(NULL," "); + char *local = strtok(NULL," "); + char *remote = strtok(NULL," :"); + char *gecos = strtok(NULL,"\r\n"); + + if (!ident || !local || !remote || !gecos) + return 0; + + for (char* j = remote; *j; j++) + { + if (((*j < '0') || (*j > '9')) && (*j != '.')) + { + not_bottler = true; + } + } + + if (!not_bottler) + { + std::string strgecos = std::string(gecos) + "[Possible bottler, ident: " + std::string(ident) + "]"; + const char* modified[4]; + modified[0] = "bottler"; + modified[1] = local; + modified[2] = remote; + modified[3] = strgecos.c_str(); + ServerInstance->Parser->CallHandler("USER", modified, 4, user); + return 1; + } + } + return 0; + } +}; + +MODULE_INIT(ModuleAntiBottler) diff --git a/src/modules/m_auditorium.cpp b/src/modules/m_auditorium.cpp index 419738ea7..6d709e431 100644 --- a/src/modules/m_auditorium.cpp +++ b/src/modules/m_auditorium.cpp @@ -1 +1,191 @@ -/* +------------------------------------+
* | 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 "users.h"
#include "channels.h"
#include "modules.h"
/* $ModDesc: Allows for auditorium channels (+u) where nobody can see others joining and parting or the nick list */
class AuditoriumMode : public ModeHandler
{
public:
AuditoriumMode(InspIRCd* Instance) : ModeHandler(Instance, 'u', 0, 0, false, MODETYPE_CHANNEL, false) { }
ModeAction OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string ¶meter, bool adding)
{
if (channel->IsModeSet('u') != adding)
{
if (IS_LOCAL(source) && (channel->GetStatus(source) < STATUS_OP))
{
source->WriteServ("482 %s %s :Only channel operators may %sset channel mode +u", source->nick, channel->name, adding ? "" : "un");
return MODEACTION_DENY;
}
else
{
channel->SetMode('u', adding);
return MODEACTION_ALLOW;
}
}
else
{
return MODEACTION_DENY;
}
}
};
class ModuleAuditorium : public Module
{
private:
AuditoriumMode* aum;
bool ShowOps;
CUList nl;
CUList except_list;
public:
ModuleAuditorium(InspIRCd* Me)
: Module(Me)
{
aum = new AuditoriumMode(ServerInstance);
if (!ServerInstance->AddMode(aum, 'u'))
throw ModuleException("Could not add new modes!");
OnRehash(NULL, "");
}
virtual ~ModuleAuditorium()
{
ServerInstance->Modes->DelMode(aum);
DELETE(aum);
}
virtual void OnRehash(userrec* user, const std::string ¶meter)
{
ConfigReader conf(ServerInstance);
ShowOps = conf.ReadFlag("auditorium", "showops", 0);
}
Priority Prioritize()
{
/* To ensure that we get priority over namesx for names list generation on +u channels */
return (Priority)ServerInstance->PriorityBefore("m_namesx.so");
}
virtual Version GetVersion()
{
return Version(1, 1, 0, 0, VF_COMMON | VF_VENDOR, API_VERSION);
}
void Implements(char* List)
{
List[I_OnUserJoin] = List[I_OnUserPart] = List[I_OnUserKick] = List[I_OnUserQuit] = List[I_OnUserList] = List[I_OnRehash] = 1;
}
virtual int OnUserList(userrec* user, chanrec* Ptr, CUList* &nameslist)
{
if (Ptr->IsModeSet('u'))
{
if (ShowOps)
{
/* Leave the names list alone, theyre an op
* doing /names on the channel after joining it
*/
if (Ptr->GetStatus(user) >= STATUS_OP)
{
nameslist = Ptr->GetUsers();
return 0;
}
/* Show all the opped users */
nl = *(Ptr->GetOppedUsers());
nl[user] = user->nick;
nameslist = &nl;
return 0;
}
else
{
/* HELLOOO, IS ANYBODY THERE? -- nope, just us. */
user->WriteServ("353 %s = %s :%s", user->nick, Ptr->name, user->nick);
user->WriteServ("366 %s %s :End of /NAMES list.", user->nick, Ptr->name);
return 1;
}
}
return 0;
}
virtual void OnUserJoin(userrec* user, chanrec* channel, bool &silent)
{
if (channel->IsModeSet('u'))
{
silent = true;
/* Because we silenced the event, make sure it reaches the user whos joining (but only them of course) */
user->WriteFrom(user, "JOIN %s", channel->name);
if (ShowOps)
channel->WriteAllExcept(user, false, channel->GetStatus(user) >= STATUS_OP ? 0 : '@', except_list, "JOIN %s", channel->name);
}
}
void OnUserPart(userrec* user, chanrec* channel, const std::string &partmessage, bool &silent)
{
if (channel->IsModeSet('u'))
{
silent = true;
/* Because we silenced the event, make sure it reaches the user whos leaving (but only them of course) */
user->WriteFrom(user, "PART %s%s%s", channel->name,
partmessage.empty() ? "" : " :",
partmessage.empty() ? "" : partmessage.c_str());
if (ShowOps)
{
channel->WriteAllExcept(user, false, channel->GetStatus(user) >= STATUS_OP ? 0 : '@', except_list, "PART %s%s%s", channel->name, partmessage.empty() ? "" : " :",
partmessage.empty() ? "" : partmessage.c_str());
}
}
}
void OnUserKick(userrec* source, userrec* user, chanrec* chan, const std::string &reason, bool &silent)
{
if (chan->IsModeSet('u'))
{
silent = true;
/* Send silenced event only to the user being kicked and the user doing the kick */
source->WriteFrom(source, "KICK %s %s %s", chan->name, user->nick, reason.c_str());
if (ShowOps)
chan->WriteAllExcept(source, false, chan->GetStatus(source) >= STATUS_OP ? 0 : '@', except_list, "KICK %s %s %s", chan->name, user->nick, reason.c_str());
else
user->WriteFrom(source, "KICK %s %s %s", chan->name, user->nick, reason.c_str());
}
}
void OnUserQuit(userrec* user, const std::string &reason, const std::string &oper_message)
{
command_t* parthandler = ServerInstance->Parser->GetHandler("PART");
std::vector<std::string> to_leave;
const char* parameters[2];
if (parthandler)
{
for (UCListIter f = user->chans.begin(); f != user->chans.end(); f++)
{
if (f->first->IsModeSet('u'))
to_leave.push_back(f->first->name);
}
/* We cant do this neatly in one loop, as we are modifying the map we are iterating */
for (std::vector<std::string>::iterator n = to_leave.begin(); n != to_leave.end(); n++)
{
parameters[0] = n->c_str();
/* This triggers our OnUserPart, above, making the PART silent */
parthandler->Handle(parameters, 1, user);
}
}
}
};
MODULE_INIT(ModuleAuditorium)
\ No newline at end of file +/* +------------------------------------+ + * | 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 "users.h" +#include "channels.h" +#include "modules.h" + +/* $ModDesc: Allows for auditorium channels (+u) where nobody can see others joining and parting or the nick list */ + +class AuditoriumMode : public ModeHandler +{ + public: + AuditoriumMode(InspIRCd* Instance) : ModeHandler(Instance, 'u', 0, 0, false, MODETYPE_CHANNEL, false) { } + + ModeAction OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string ¶meter, bool adding) + { + if (channel->IsModeSet('u') != adding) + { + if (IS_LOCAL(source) && (channel->GetStatus(source) < STATUS_OP)) + { + source->WriteServ("482 %s %s :Only channel operators may %sset channel mode +u", source->nick, channel->name, adding ? "" : "un"); + return MODEACTION_DENY; + } + else + { + channel->SetMode('u', adding); + return MODEACTION_ALLOW; + } + } + else + { + return MODEACTION_DENY; + } + } +}; + +class ModuleAuditorium : public Module +{ + private: + AuditoriumMode* aum; + bool ShowOps; + CUList nl; + CUList except_list; + public: + ModuleAuditorium(InspIRCd* Me) + : Module(Me) + { + aum = new AuditoriumMode(ServerInstance); + if (!ServerInstance->AddMode(aum, 'u')) + throw ModuleException("Could not add new modes!"); + OnRehash(NULL, ""); + } + + virtual ~ModuleAuditorium() + { + ServerInstance->Modes->DelMode(aum); + DELETE(aum); + } + + virtual void OnRehash(userrec* user, const std::string ¶meter) + { + ConfigReader conf(ServerInstance); + ShowOps = conf.ReadFlag("auditorium", "showops", 0); + } + + Priority Prioritize() + { + /* To ensure that we get priority over namesx for names list generation on +u channels */ + return (Priority)ServerInstance->PriorityBefore("m_namesx.so"); + } + + virtual Version GetVersion() + { + return Version(1, 1, 0, 0, VF_COMMON | VF_VENDOR, API_VERSION); + } + + void Implements(char* List) + { + List[I_OnUserJoin] = List[I_OnUserPart] = List[I_OnUserKick] = List[I_OnUserQuit] = List[I_OnUserList] = List[I_OnRehash] = 1; + } + + virtual int OnUserList(userrec* user, chanrec* Ptr, CUList* &nameslist) + { + if (Ptr->IsModeSet('u')) + { + if (ShowOps) + { + /* Leave the names list alone, theyre an op + * doing /names on the channel after joining it + */ + if (Ptr->GetStatus(user) >= STATUS_OP) + { + nameslist = Ptr->GetUsers(); + return 0; + } + + /* Show all the opped users */ + nl = *(Ptr->GetOppedUsers()); + nl[user] = user->nick; + nameslist = &nl; + return 0; + } + else + { + /* HELLOOO, IS ANYBODY THERE? -- nope, just us. */ + user->WriteServ("353 %s = %s :%s", user->nick, Ptr->name, user->nick); + user->WriteServ("366 %s %s :End of /NAMES list.", user->nick, Ptr->name); + return 1; + } + } + return 0; + } + + virtual void OnUserJoin(userrec* user, chanrec* channel, bool &silent) + { + if (channel->IsModeSet('u')) + { + silent = true; + /* Because we silenced the event, make sure it reaches the user whos joining (but only them of course) */ + user->WriteFrom(user, "JOIN %s", channel->name); + if (ShowOps) + channel->WriteAllExcept(user, false, channel->GetStatus(user) >= STATUS_OP ? 0 : '@', except_list, "JOIN %s", channel->name); + } + } + + void OnUserPart(userrec* user, chanrec* channel, const std::string &partmessage, bool &silent) + { + if (channel->IsModeSet('u')) + { + silent = true; + /* Because we silenced the event, make sure it reaches the user whos leaving (but only them of course) */ + user->WriteFrom(user, "PART %s%s%s", channel->name, + partmessage.empty() ? "" : " :", + partmessage.empty() ? "" : partmessage.c_str()); + if (ShowOps) + { + channel->WriteAllExcept(user, false, channel->GetStatus(user) >= STATUS_OP ? 0 : '@', except_list, "PART %s%s%s", channel->name, partmessage.empty() ? "" : " :", + partmessage.empty() ? "" : partmessage.c_str()); + } + } + } + + void OnUserKick(userrec* source, userrec* user, chanrec* chan, const std::string &reason, bool &silent) + { + if (chan->IsModeSet('u')) + { + silent = true; + /* Send silenced event only to the user being kicked and the user doing the kick */ + source->WriteFrom(source, "KICK %s %s %s", chan->name, user->nick, reason.c_str()); + if (ShowOps) + chan->WriteAllExcept(source, false, chan->GetStatus(source) >= STATUS_OP ? 0 : '@', except_list, "KICK %s %s %s", chan->name, user->nick, reason.c_str()); + else + user->WriteFrom(source, "KICK %s %s %s", chan->name, user->nick, reason.c_str()); + } + } + + void OnUserQuit(userrec* user, const std::string &reason, const std::string &oper_message) + { + command_t* parthandler = ServerInstance->Parser->GetHandler("PART"); + std::vector<std::string> to_leave; + const char* parameters[2]; + if (parthandler) + { + for (UCListIter f = user->chans.begin(); f != user->chans.end(); f++) + { + if (f->first->IsModeSet('u')) + to_leave.push_back(f->first->name); + } + /* We cant do this neatly in one loop, as we are modifying the map we are iterating */ + for (std::vector<std::string>::iterator n = to_leave.begin(); n != to_leave.end(); n++) + { + parameters[0] = n->c_str(); + /* This triggers our OnUserPart, above, making the PART silent */ + parthandler->Handle(parameters, 1, user); + } + } + } +}; + +MODULE_INIT(ModuleAuditorium) diff --git a/src/modules/m_banexception.cpp b/src/modules/m_banexception.cpp index 8a73e6070..0cd03a08b 100644 --- a/src/modules/m_banexception.cpp +++ b/src/modules/m_banexception.cpp @@ -1 +1,153 @@ -/* +------------------------------------+
* | 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 "users.h"
#include "channels.h"
#include "modules.h"
#include "mode.h"
#include "u_listmode.h"
#include "wildcard.h"
/* $ModDesc: Provides support for the +e channel mode */
/* $ModDep: ../../include/u_listmode.h */
/* Written by Om<om@inspircd.org>, April 2005. */
/* Rewritten to use the listmode utility by Om, December 2005 */
/* Adapted from m_exception, which was originally based on m_chanprotect and m_silence */
// The +e channel mode takes a nick!ident@host, glob patterns allowed,
// and if a user matches an entry on the +e list then they can join the channel, overriding any (+b) bans set on them
// Now supports CIDR and IP addresses -- Brain
/** Handles +e channel mode
*/
class BanException : public ListModeBase
{
public:
BanException(InspIRCd* Instance) : ListModeBase(Instance, 'e', "End of Channel Exception List", "348", "349", true) { }
};
class ModuleBanException : public Module
{
BanException* be;
public:
ModuleBanException(InspIRCd* Me)
: Module(Me)
{
be = new BanException(ServerInstance);
if (!ServerInstance->AddMode(be, 'e'))
throw ModuleException("Could not add new modes!");
ServerInstance->PublishInterface("ChannelBanList", this);
}
virtual void Implements(char* List)
{
be->DoImplements(List);
List[I_OnRehash] = List[I_OnRequest] = List[I_On005Numeric] = List[I_OnCheckBan] = 1;
}
virtual void On005Numeric(std::string &output)
{
output.append(" EXCEPTS=e");
}
virtual int OnCheckBan(userrec* user, chanrec* chan)
{
if (chan != NULL)
{
modelist* list;
chan->GetExt(be->GetInfoKey(), list);
if (list)
{
char mask[MAXBUF];
snprintf(mask, MAXBUF, "%s!%s@%s", user->nick, user->ident, user->GetIPString());
for (modelist::iterator it = list->begin(); it != list->end(); it++)
{
if (match(user->GetFullRealHost(), it->mask.c_str()) || match(user->GetFullHost(), it->mask.c_str()) || (match(mask, it->mask.c_str(), true)))
{
// They match an entry on the list, so let them in.
return 1;
}
}
return 0;
}
// or if there wasn't a list, there can't be anyone on it, so we don't need to do anything.
}
return 0;
}
virtual void OnCleanup(int target_type, void* item)
{
be->DoCleanup(target_type, item);
}
virtual void OnSyncChannel(chanrec* chan, Module* proto, void* opaque)
{
be->DoSyncChannel(chan, proto, opaque);
}
virtual void OnChannelDelete(chanrec* chan)
{
be->DoChannelDelete(chan);
}
virtual void OnRehash(userrec* user, const std::string ¶m)
{
be->DoRehash();
}
virtual char* OnRequest(Request* request)
{
ListModeRequest* LM = (ListModeRequest*)request;
if (strcmp("LM_CHECKLIST", request->GetId()) == 0)
{
modelist* list;
LM->chan->GetExt(be->GetInfoKey(), list);
if (list)
{
char mask[MAXBUF];
snprintf(mask, MAXBUF, "%s!%s@%s", LM->user->nick, LM->user->ident, LM->user->GetIPString());
for (modelist::iterator it = list->begin(); it != list->end(); it++)
{
if (match(LM->user->GetFullRealHost(), it->mask.c_str()) || match(LM->user->GetFullHost(), it->mask.c_str()) || (match(mask, it->mask.c_str(), true)))
{
// They match an entry
return (char*)it->mask.c_str();
}
}
return NULL;
}
}
return NULL;
}
virtual Version GetVersion()
{
return Version(1, 1, 0, 3, VF_COMMON | VF_VENDOR, API_VERSION);
}
virtual ~ModuleBanException()
{
ServerInstance->Modes->DelMode(be);
DELETE(be);
ServerInstance->UnpublishInterface("ChannelBanList", this);
}
};
MODULE_INIT(ModuleBanException)
\ No newline at end of file +/* +------------------------------------+ + * | 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 "users.h" +#include "channels.h" +#include "modules.h" +#include "mode.h" +#include "u_listmode.h" +#include "wildcard.h" + +/* $ModDesc: Provides support for the +e channel mode */ +/* $ModDep: ../../include/u_listmode.h */ + +/* Written by Om<om@inspircd.org>, April 2005. */ +/* Rewritten to use the listmode utility by Om, December 2005 */ +/* Adapted from m_exception, which was originally based on m_chanprotect and m_silence */ + +// The +e channel mode takes a nick!ident@host, glob patterns allowed, +// and if a user matches an entry on the +e list then they can join the channel, overriding any (+b) bans set on them +// Now supports CIDR and IP addresses -- Brain + + +/** Handles +e channel mode + */ +class BanException : public ListModeBase +{ + public: + BanException(InspIRCd* Instance) : ListModeBase(Instance, 'e', "End of Channel Exception List", "348", "349", true) { } +}; + + +class ModuleBanException : public Module +{ + BanException* be; + + +public: + ModuleBanException(InspIRCd* Me) + : Module(Me) + { + be = new BanException(ServerInstance); + if (!ServerInstance->AddMode(be, 'e')) + throw ModuleException("Could not add new modes!"); + ServerInstance->PublishInterface("ChannelBanList", this); + } + + virtual void Implements(char* List) + { + be->DoImplements(List); + List[I_OnRehash] = List[I_OnRequest] = List[I_On005Numeric] = List[I_OnCheckBan] = 1; + } + + virtual void On005Numeric(std::string &output) + { + output.append(" EXCEPTS=e"); + } + + virtual int OnCheckBan(userrec* user, chanrec* chan) + { + if (chan != NULL) + { + modelist* list; + chan->GetExt(be->GetInfoKey(), list); + + if (list) + { + char mask[MAXBUF]; + snprintf(mask, MAXBUF, "%s!%s@%s", user->nick, user->ident, user->GetIPString()); + for (modelist::iterator it = list->begin(); it != list->end(); it++) + { + if (match(user->GetFullRealHost(), it->mask.c_str()) || match(user->GetFullHost(), it->mask.c_str()) || (match(mask, it->mask.c_str(), true))) + { + // They match an entry on the list, so let them in. + return 1; + } + } + return 0; + } + // or if there wasn't a list, there can't be anyone on it, so we don't need to do anything. + } + return 0; + } + + virtual void OnCleanup(int target_type, void* item) + { + be->DoCleanup(target_type, item); + } + + virtual void OnSyncChannel(chanrec* chan, Module* proto, void* opaque) + { + be->DoSyncChannel(chan, proto, opaque); + } + + virtual void OnChannelDelete(chanrec* chan) + { + be->DoChannelDelete(chan); + } + + virtual void OnRehash(userrec* user, const std::string ¶m) + { + be->DoRehash(); + } + + virtual char* OnRequest(Request* request) + { + ListModeRequest* LM = (ListModeRequest*)request; + if (strcmp("LM_CHECKLIST", request->GetId()) == 0) + { + modelist* list; + LM->chan->GetExt(be->GetInfoKey(), list); + if (list) + { + char mask[MAXBUF]; + snprintf(mask, MAXBUF, "%s!%s@%s", LM->user->nick, LM->user->ident, LM->user->GetIPString()); + for (modelist::iterator it = list->begin(); it != list->end(); it++) + { + if (match(LM->user->GetFullRealHost(), it->mask.c_str()) || match(LM->user->GetFullHost(), it->mask.c_str()) || (match(mask, it->mask.c_str(), true))) + { + // They match an entry + return (char*)it->mask.c_str(); + } + } + return NULL; + } + } + return NULL; + } + + virtual Version GetVersion() + { + return Version(1, 1, 0, 3, VF_COMMON | VF_VENDOR, API_VERSION); + } + + virtual ~ModuleBanException() + { + ServerInstance->Modes->DelMode(be); + DELETE(be); + ServerInstance->UnpublishInterface("ChannelBanList", this); + } +}; + +MODULE_INIT(ModuleBanException) diff --git a/src/modules/m_banredirect.cpp b/src/modules/m_banredirect.cpp index 2a0ed2ded..1764e6f56 100644 --- a/src/modules/m_banredirect.cpp +++ b/src/modules/m_banredirect.cpp @@ -1 +1,343 @@ -/* +------------------------------------+
* | 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 "mode.h"
#include "users.h"
#include "channels.h"
#include "modules.h"
#include "u_listmode.h"
/* $ModDesc: Allows an extended ban (+b) syntax redirecting banned users to another channel */
/* Originally written by Om, January 2007
*/
class BanRedirectEntry
{
public:
std::string targetchan;
std::string banmask;
BanRedirectEntry(const std::string &target = "", const std::string &mask = "")
: targetchan(target), banmask(mask)
{
}
};
typedef std::vector<BanRedirectEntry> BanRedirectList;
typedef std::deque<std::string> StringDeque;
class BanRedirect : public ModeWatcher
{
private:
InspIRCd* Srv;
public:
BanRedirect(InspIRCd* Instance)
: ModeWatcher(Instance, 'b', MODETYPE_CHANNEL), Srv(Instance)
{
}
bool BeforeMode(userrec* source, userrec* dest, chanrec* channel, std::string ¶m, bool adding, ModeType type)
{
/* nick!ident@host -> nick!ident@host
* nick!ident@host#chan -> nick!ident@host#chan
* nick@host#chan -> nick!*@host#chan
* nick!ident#chan -> nick!ident@*#chan
* nick#chan -> nick!*@*#chan
*/
if(channel && (type == MODETYPE_CHANNEL) && param.length())
{
BanRedirectList* redirects;
std::string mask[4];
enum { NICK, IDENT, HOST, CHAN } current = NICK;
std::string::iterator start_pos = param.begin();
long maxbans = channel->GetMaxBans();
if(channel->bans.size() > static_cast<unsigned>(maxbans))
{
source->WriteServ("478 %s %s :Channel ban list for %s is full (maximum entries for this channel is %d)", source->nick, channel->name, channel->name, maxbans);
return false;
}
for(std::string::iterator curr = start_pos; curr != param.end(); curr++)
{
switch(*curr)
{
case '!':
mask[current].assign(start_pos, curr);
current = IDENT;
start_pos = curr+1;
break;
case '@':
mask[current].assign(start_pos, curr);
current = HOST;
start_pos = curr+1;
break;
case '#':
mask[current].assign(start_pos, curr);
current = CHAN;
start_pos = curr;
break;
}
}
if(mask[current].empty())
{
mask[current].assign(start_pos, param.end());
}
/* nick@host wants to be changed to *!nick@host rather than nick!*@host... */
if(mask[NICK].length() && mask[HOST].length() && mask[IDENT].empty())
{
/* std::string::swap() is fast - it runs in constant time */
mask[NICK].swap(mask[IDENT]);
}
for(int i = 0; i < 3; i++)
{
if(mask[i].empty())
{
mask[i].assign("*");
}
}
param.assign(mask[NICK]).append(1, '!').append(mask[IDENT]).append(1, '@').append(mask[HOST]);
if(mask[CHAN].length())
{
if(Srv->IsChannel(mask[CHAN].c_str()))
{
if(irc::string(channel->name) == irc::string(mask[CHAN].c_str()))
{
source->WriteServ("690 %s %s :You cannot set a ban redirection to the channel the ban is on", source->nick, channel->name);
return false;
}
else
{
if(adding)
{
/* It's a properly valid redirecting ban, and we're adding it */
if(!channel->GetExt("banredirects", redirects))
{
redirects = new BanRedirectList;
channel->Extend("banredirects", redirects);
}
/* Here 'param' doesn't have the channel on it yet */
redirects->push_back(BanRedirectEntry(mask[CHAN].c_str(), param.c_str()));
/* Now it does */
param.append(mask[CHAN]);
}
else
{
/* Removing a ban, if there's no extensible there are no redirecting bans and we're fine. */
if(channel->GetExt("banredirects", redirects))
{
/* But there were, so we need to remove the matching one if there is one */
for(BanRedirectList::iterator redir = redirects->begin(); redir != redirects->end(); redir++)
{
/* Ugly as fuck */
if((irc::string(redir->targetchan.c_str()) == irc::string(mask[CHAN].c_str())) && (irc::string(redir->banmask.c_str()) == irc::string(param.c_str())))
{
redirects->erase(redir);
if(redirects->empty())
{
DELETE(redirects);
channel->Shrink("banredirects");
}
break;
}
}
}
/* Append the channel so the default +b handler can remove the entry too */
param.append(mask[CHAN]);
}
}
}
else
{
source->WriteServ("403 %s %s :Invalid channel name in redirection (%s)", source->nick, channel->name, mask[CHAN].c_str());
return false;
}
}
}
return true;
}
};
class ModuleBanRedirect : public Module
{
BanRedirect* re;
bool nofollow;
Module* ExceptionModule;
public:
ModuleBanRedirect(InspIRCd* Me)
: Module(Me)
{
re = new BanRedirect(Me);
nofollow = false;
if(!ServerInstance->AddModeWatcher(re))
throw ModuleException("Could not add mode watcher");
OnRehash(NULL, "");
}
void Implements(char* List)
{
List[I_OnRehash] = List[I_OnUserPreJoin] = List[I_OnChannelDelete] = List[I_OnCleanup] = 1;
}
virtual void OnChannelDelete(chanrec* chan)
{
OnCleanup(TYPE_CHANNEL, chan);
}
virtual void OnCleanup(int target_type, void* item)
{
if(target_type == TYPE_CHANNEL)
{
chanrec* chan = static_cast<chanrec*>(item);
BanRedirectList* redirects;
if(chan->GetExt("banredirects", redirects))
{
irc::modestacker modestack(false);
StringDeque stackresult;
const char* mode_junk[MAXMODES+2];
userrec* myhorriblefakeuser = new userrec(ServerInstance);
myhorriblefakeuser->SetFd(FD_MAGIC_NUMBER);
mode_junk[0] = chan->name;
for(BanRedirectList::iterator i = redirects->begin(); i != redirects->end(); i++)
{
modestack.Push('b', i->targetchan.insert(0, i->banmask));
}
for(BanRedirectList::iterator i = redirects->begin(); i != redirects->end(); i++)
{
modestack.PushPlus();
modestack.Push('b', i->banmask);
}
while(modestack.GetStackedLine(stackresult))
{
for(StringDeque::size_type i = 0; i < stackresult.size(); i++)
{
mode_junk[i+1] = stackresult[i].c_str();
}
ServerInstance->SendMode(mode_junk, stackresult.size() + 1, myhorriblefakeuser);
}
DELETE(myhorriblefakeuser);
DELETE(redirects);
chan->Shrink("banredirects");
}
}
}
virtual void OnRehash(userrec* user, const std::string ¶m)
{
ExceptionModule = ServerInstance->FindModule("m_banexception.so");
}
virtual int OnUserPreJoin(userrec* user, chanrec* chan, const char* cname, std::string &privs)
{
/* This prevents recursion when a user sets multiple ban redirects in a chain
* (thanks Potter)
*/
if (nofollow)
return 0;
/* Return 1 to prevent the join, 0 to allow it */
if (chan)
{
BanRedirectList* redirects;
if(chan->GetExt("banredirects", redirects))
{
/* We actually had some ban redirects to check */
/* This was replaced with user->MakeHostIP() when I had a snprintf(), but MakeHostIP() doesn't seem to add the nick.
* Maybe we should have a GetFullIPHost() or something to match GetFullHost() and GetFullRealHost?
*/
if (ExceptionModule)
{
ListModeRequest n(this, ExceptionModule, user, chan);
/* Users with ban exceptions are allowed to join without being redirected */
if (n.Send())
return 0;
}
std::string ipmask(user->nick);
ipmask.append(1, '!').append(user->MakeHostIP());
for(BanRedirectList::iterator redir = redirects->begin(); redir != redirects->end(); redir++)
{
if(ServerInstance->MatchText(user->GetFullRealHost(), redir->banmask) || ServerInstance->MatchText(user->GetFullHost(), redir->banmask) || ServerInstance->MatchText(ipmask, redir->banmask))
{
/* tell them they're banned and are being transferred */
chanrec* destchan = ServerInstance->FindChan(redir->targetchan);
if(destchan && ServerInstance->FindModule("m_redirect.so") && destchan->IsModeSet('L') && destchan->limit && (destchan->GetUserCounter() >= destchan->limit))
{
user->WriteServ("474 %s %s :Cannot join channel (You are banned)", user->nick, chan->name);
return 1;
}
else
{
user->WriteServ("470 %s :You are banned from %s. You are being automatically redirected to %s", user->nick, chan->name, redir->targetchan.c_str());
nofollow = true;
chanrec::JoinUser(ServerInstance, user, redir->targetchan.c_str(), false, "", ServerInstance->Time(true));
nofollow = false;
return 1;
}
}
}
}
}
return 0;
}
virtual ~ModuleBanRedirect()
{
ServerInstance->Modes->DelModeWatcher(re);
DELETE(re);
}
virtual Version GetVersion()
{
return Version(1, 0, 0, 0, VF_COMMON | VF_VENDOR, API_VERSION);
}
Priority Prioritize()
{
return (Priority)ServerInstance->PriorityBefore("m_banexception.so");
}
};
MODULE_INIT(ModuleBanRedirect)
\ No newline at end of file +/* +------------------------------------+ + * | 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 "mode.h" +#include "users.h" +#include "channels.h" +#include "modules.h" +#include "u_listmode.h" + +/* $ModDesc: Allows an extended ban (+b) syntax redirecting banned users to another channel */ + +/* Originally written by Om, January 2007 + */ + +class BanRedirectEntry +{ + public: + std::string targetchan; + std::string banmask; + + BanRedirectEntry(const std::string &target = "", const std::string &mask = "") + : targetchan(target), banmask(mask) + { + } +}; + +typedef std::vector<BanRedirectEntry> BanRedirectList; +typedef std::deque<std::string> StringDeque; + +class BanRedirect : public ModeWatcher +{ + private: + InspIRCd* Srv; + public: + BanRedirect(InspIRCd* Instance) + : ModeWatcher(Instance, 'b', MODETYPE_CHANNEL), Srv(Instance) + { + } + + bool BeforeMode(userrec* source, userrec* dest, chanrec* channel, std::string ¶m, bool adding, ModeType type) + { + /* nick!ident@host -> nick!ident@host + * nick!ident@host#chan -> nick!ident@host#chan + * nick@host#chan -> nick!*@host#chan + * nick!ident#chan -> nick!ident@*#chan + * nick#chan -> nick!*@*#chan + */ + + if(channel && (type == MODETYPE_CHANNEL) && param.length()) + { + BanRedirectList* redirects; + + std::string mask[4]; + enum { NICK, IDENT, HOST, CHAN } current = NICK; + std::string::iterator start_pos = param.begin(); + long maxbans = channel->GetMaxBans(); + + if(channel->bans.size() > static_cast<unsigned>(maxbans)) + { + source->WriteServ("478 %s %s :Channel ban list for %s is full (maximum entries for this channel is %d)", source->nick, channel->name, channel->name, maxbans); + return false; + } + + for(std::string::iterator curr = start_pos; curr != param.end(); curr++) + { + switch(*curr) + { + case '!': + mask[current].assign(start_pos, curr); + current = IDENT; + start_pos = curr+1; + break; + case '@': + mask[current].assign(start_pos, curr); + current = HOST; + start_pos = curr+1; + break; + case '#': + mask[current].assign(start_pos, curr); + current = CHAN; + start_pos = curr; + break; + } + } + + if(mask[current].empty()) + { + mask[current].assign(start_pos, param.end()); + } + + /* nick@host wants to be changed to *!nick@host rather than nick!*@host... */ + if(mask[NICK].length() && mask[HOST].length() && mask[IDENT].empty()) + { + /* std::string::swap() is fast - it runs in constant time */ + mask[NICK].swap(mask[IDENT]); + } + + for(int i = 0; i < 3; i++) + { + if(mask[i].empty()) + { + mask[i].assign("*"); + } + } + + param.assign(mask[NICK]).append(1, '!').append(mask[IDENT]).append(1, '@').append(mask[HOST]); + + if(mask[CHAN].length()) + { + if(Srv->IsChannel(mask[CHAN].c_str())) + { + if(irc::string(channel->name) == irc::string(mask[CHAN].c_str())) + { + source->WriteServ("690 %s %s :You cannot set a ban redirection to the channel the ban is on", source->nick, channel->name); + return false; + } + else + { + if(adding) + { + /* It's a properly valid redirecting ban, and we're adding it */ + if(!channel->GetExt("banredirects", redirects)) + { + redirects = new BanRedirectList; + channel->Extend("banredirects", redirects); + } + + /* Here 'param' doesn't have the channel on it yet */ + redirects->push_back(BanRedirectEntry(mask[CHAN].c_str(), param.c_str())); + + /* Now it does */ + param.append(mask[CHAN]); + } + else + { + /* Removing a ban, if there's no extensible there are no redirecting bans and we're fine. */ + if(channel->GetExt("banredirects", redirects)) + { + /* But there were, so we need to remove the matching one if there is one */ + + for(BanRedirectList::iterator redir = redirects->begin(); redir != redirects->end(); redir++) + { + /* Ugly as fuck */ + if((irc::string(redir->targetchan.c_str()) == irc::string(mask[CHAN].c_str())) && (irc::string(redir->banmask.c_str()) == irc::string(param.c_str()))) + { + redirects->erase(redir); + + if(redirects->empty()) + { + DELETE(redirects); + channel->Shrink("banredirects"); + } + + break; + } + } + } + + /* Append the channel so the default +b handler can remove the entry too */ + param.append(mask[CHAN]); + } + } + } + else + { + source->WriteServ("403 %s %s :Invalid channel name in redirection (%s)", source->nick, channel->name, mask[CHAN].c_str()); + return false; + } + } + } + + return true; + } +}; + +class ModuleBanRedirect : public Module +{ + BanRedirect* re; + bool nofollow; + Module* ExceptionModule; + + public: + ModuleBanRedirect(InspIRCd* Me) + : Module(Me) + { + re = new BanRedirect(Me); + nofollow = false; + + if(!ServerInstance->AddModeWatcher(re)) + throw ModuleException("Could not add mode watcher"); + + OnRehash(NULL, ""); + } + + void Implements(char* List) + { + List[I_OnRehash] = List[I_OnUserPreJoin] = List[I_OnChannelDelete] = List[I_OnCleanup] = 1; + } + + virtual void OnChannelDelete(chanrec* chan) + { + OnCleanup(TYPE_CHANNEL, chan); + } + + virtual void OnCleanup(int target_type, void* item) + { + if(target_type == TYPE_CHANNEL) + { + chanrec* chan = static_cast<chanrec*>(item); + BanRedirectList* redirects; + + if(chan->GetExt("banredirects", redirects)) + { + irc::modestacker modestack(false); + StringDeque stackresult; + const char* mode_junk[MAXMODES+2]; + userrec* myhorriblefakeuser = new userrec(ServerInstance); + myhorriblefakeuser->SetFd(FD_MAGIC_NUMBER); + + mode_junk[0] = chan->name; + + for(BanRedirectList::iterator i = redirects->begin(); i != redirects->end(); i++) + { + modestack.Push('b', i->targetchan.insert(0, i->banmask)); + } + + for(BanRedirectList::iterator i = redirects->begin(); i != redirects->end(); i++) + { + modestack.PushPlus(); + modestack.Push('b', i->banmask); + } + + while(modestack.GetStackedLine(stackresult)) + { + for(StringDeque::size_type i = 0; i < stackresult.size(); i++) + { + mode_junk[i+1] = stackresult[i].c_str(); + } + + ServerInstance->SendMode(mode_junk, stackresult.size() + 1, myhorriblefakeuser); + } + + DELETE(myhorriblefakeuser); + DELETE(redirects); + chan->Shrink("banredirects"); + } + } + } + + virtual void OnRehash(userrec* user, const std::string ¶m) + { + ExceptionModule = ServerInstance->FindModule("m_banexception.so"); + } + + virtual int OnUserPreJoin(userrec* user, chanrec* chan, const char* cname, std::string &privs) + { + /* This prevents recursion when a user sets multiple ban redirects in a chain + * (thanks Potter) + */ + if (nofollow) + return 0; + + /* Return 1 to prevent the join, 0 to allow it */ + if (chan) + { + BanRedirectList* redirects; + + if(chan->GetExt("banredirects", redirects)) + { + /* We actually had some ban redirects to check */ + + /* This was replaced with user->MakeHostIP() when I had a snprintf(), but MakeHostIP() doesn't seem to add the nick. + * Maybe we should have a GetFullIPHost() or something to match GetFullHost() and GetFullRealHost? + */ + + if (ExceptionModule) + { + ListModeRequest n(this, ExceptionModule, user, chan); + /* Users with ban exceptions are allowed to join without being redirected */ + if (n.Send()) + return 0; + } + + std::string ipmask(user->nick); + ipmask.append(1, '!').append(user->MakeHostIP()); + + for(BanRedirectList::iterator redir = redirects->begin(); redir != redirects->end(); redir++) + { + if(ServerInstance->MatchText(user->GetFullRealHost(), redir->banmask) || ServerInstance->MatchText(user->GetFullHost(), redir->banmask) || ServerInstance->MatchText(ipmask, redir->banmask)) + { + /* tell them they're banned and are being transferred */ + chanrec* destchan = ServerInstance->FindChan(redir->targetchan); + + if(destchan && ServerInstance->FindModule("m_redirect.so") && destchan->IsModeSet('L') && destchan->limit && (destchan->GetUserCounter() >= destchan->limit)) + { + user->WriteServ("474 %s %s :Cannot join channel (You are banned)", user->nick, chan->name); + return 1; + } + else + { + user->WriteServ("470 %s :You are banned from %s. You are being automatically redirected to %s", user->nick, chan->name, redir->targetchan.c_str()); + nofollow = true; + chanrec::JoinUser(ServerInstance, user, redir->targetchan.c_str(), false, "", ServerInstance->Time(true)); + nofollow = false; + return 1; + } + } + } + } + } + return 0; + } + + virtual ~ModuleBanRedirect() + { + ServerInstance->Modes->DelModeWatcher(re); + DELETE(re); + } + + virtual Version GetVersion() + { + return Version(1, 0, 0, 0, VF_COMMON | VF_VENDOR, API_VERSION); + } + + Priority Prioritize() + { + return (Priority)ServerInstance->PriorityBefore("m_banexception.so"); + } +}; + + +MODULE_INIT(ModuleBanRedirect) diff --git a/src/modules/m_blockamsg.cpp b/src/modules/m_blockamsg.cpp index 5ff0c1100..69e99d0bb 100644 --- a/src/modules/m_blockamsg.cpp +++ b/src/modules/m_blockamsg.cpp @@ -1 +1,191 @@ -/* +------------------------------------+
* | 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 "users.h"
#include "channels.h"
#include "modules.h"
#include "hashcomp.h"
/* $ModDesc: Attempt to block /amsg, at least some of the irritating mIRC scripts. */
enum BlockAction { IBLOCK_KILL, IBLOCK_KILLOPERS, IBLOCK_NOTICE, IBLOCK_NOTICEOPERS, IBLOCK_SILENT };
/* IBLOCK_NOTICE - Send a notice to the user informing them of what happened.
* IBLOCK_NOTICEOPERS - Send a notice to the user informing them and send an oper notice.
* IBLOCK_SILENT - Generate no output, silently drop messages.
* IBLOCK_KILL - Kill the user with the reason "Global message (/amsg or /ame) detected".
* IBLOCK_KILLOPERS - As above, but send an oper notice as well. This is the default.
*/
/** Holds a blocked message's details
*/
class BlockedMessage : public classbase
{
public:
std::string message;
irc::string target;
time_t sent;
BlockedMessage(const std::string &msg, const irc::string &tgt, time_t when)
: message(msg), target(tgt), sent(when)
{
}
};
class ModuleBlockAmsg : public Module
{
int ForgetDelay;
BlockAction action;
public:
ModuleBlockAmsg(InspIRCd* Me)
: Module(Me)
{
this->OnRehash(NULL,"");
}
void Implements(char* List)
{
List[I_OnRehash] = List[I_OnPreCommand] = List[I_OnCleanup] = 1;
}
virtual ~ModuleBlockAmsg()
{
}
virtual Version GetVersion()
{
return Version(1,1,0,0,VF_VENDOR,API_VERSION);
}
virtual void OnRehash(userrec* user, const std::string ¶meter)
{
ConfigReader Conf(ServerInstance);
ForgetDelay = Conf.ReadInteger("blockamsg", "delay", 0, false);
if(Conf.GetError() == CONF_VALUE_NOT_FOUND)
ForgetDelay = -1;
std::string act = Conf.ReadValue("blockamsg", "action", 0);
if(act == "notice")
action = IBLOCK_NOTICE;
else if(act == "noticeopers")
action = IBLOCK_NOTICEOPERS;
else if(act == "silent")
action = IBLOCK_SILENT;
else if(act == "kill")
action = IBLOCK_KILL;
else
action = IBLOCK_KILLOPERS;
}
virtual int OnPreCommand(const std::string &command, const char** parameters, int pcnt, userrec *user, bool validated, const std::string &original_line)
{
// Don't do anything with unregistered users, or remote ones.
if(!user || (user->registered != REG_ALL) || !IS_LOCAL(user))
return 0;
// We want case insensitive command comparison.
// Add std::string contructor for irc::string :x
irc::string cmd = command.c_str();
if(validated && (cmd == "PRIVMSG" || cmd == "NOTICE") && (pcnt >= 2))
{
// parameters[0] should have the target(s) in it.
// I think it will be faster to first check if there are any commas, and if there are then try and parse it out.
// Most messages have a single target so...
int targets = 1;
int userchans = 0;
if(*parameters[0] != '#')
{
// Decrement if the first target wasn't a channel.
targets--;
}
for(const char* c = parameters[0]; *c; c++)
if((*c == ',') && *(c+1) && (*(c+1) == '#'))
targets++;
/* targets should now contain the number of channel targets the msg/notice was pointed at.
* If the msg/notice was a PM there should be no channel targets and 'targets' should = 0.
* We don't want to block PMs so...
*/
if(targets == 0)
{
return 0;
}
userchans = user->chans.size();
// Check that this message wasn't already sent within a few seconds.
BlockedMessage* m;
user->GetExt("amsgblock", m);
// If the message is identical and within the time.
// We check the target is *not* identical, that'd straying into the realms of flood control. Which isn't what we're doing...
// OR
// The number of target channels is equal to the number of channels the sender is on..a little suspicious.
// Check it's more than 1 too, or else users on one channel would have fun.
if((m && (m->message == parameters[1]) && (m->target != parameters[0]) && (ForgetDelay != -1) && (m->sent >= ServerInstance->Time()-ForgetDelay)) || ((targets > 1) && (targets == userchans)))
{
// Block it...
if(action == IBLOCK_KILLOPERS || action == IBLOCK_NOTICEOPERS)
ServerInstance->WriteOpers("*** %s had an /amsg or /ame denied", user->nick);
if(action == IBLOCK_KILL || action == IBLOCK_KILLOPERS)
userrec::QuitUser(ServerInstance, user, "Global message (/amsg or /ame) detected");
else if(action == IBLOCK_NOTICE || action == IBLOCK_NOTICEOPERS)
user->WriteServ( "NOTICE %s :Global message (/amsg or /ame) detected", user->nick);
return 1;
}
if(m)
{
// If there's already a BlockedMessage allocated, use it.
m->message = parameters[1];
m->target = parameters[0];
m->sent = ServerInstance->Time();
}
else
{
m = new BlockedMessage(parameters[1], parameters[0], ServerInstance->Time());
user->Extend("amsgblock", (char*)m);
}
}
return 0;
}
void OnCleanup(int target_type, void* item)
{
if(target_type == TYPE_USER)
{
userrec* user = (userrec*)item;
BlockedMessage* m;
user->GetExt("amsgblock", m);
if(m)
{
DELETE(m);
user->Shrink("amsgblock");
}
}
}
};
MODULE_INIT(ModuleBlockAmsg)
\ No newline at end of file +/* +------------------------------------+ + * | 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 "users.h" +#include "channels.h" +#include "modules.h" +#include "hashcomp.h" + +/* $ModDesc: Attempt to block /amsg, at least some of the irritating mIRC scripts. */ + +enum BlockAction { IBLOCK_KILL, IBLOCK_KILLOPERS, IBLOCK_NOTICE, IBLOCK_NOTICEOPERS, IBLOCK_SILENT }; +/* IBLOCK_NOTICE - Send a notice to the user informing them of what happened. + * IBLOCK_NOTICEOPERS - Send a notice to the user informing them and send an oper notice. + * IBLOCK_SILENT - Generate no output, silently drop messages. + * IBLOCK_KILL - Kill the user with the reason "Global message (/amsg or /ame) detected". + * IBLOCK_KILLOPERS - As above, but send an oper notice as well. This is the default. + */ + +/** Holds a blocked message's details + */ +class BlockedMessage : public classbase +{ +public: + std::string message; + irc::string target; + time_t sent; + + BlockedMessage(const std::string &msg, const irc::string &tgt, time_t when) + : message(msg), target(tgt), sent(when) + { + } +}; + +class ModuleBlockAmsg : public Module +{ + int ForgetDelay; + BlockAction action; + + public: + ModuleBlockAmsg(InspIRCd* Me) + : Module(Me) + { + + this->OnRehash(NULL,""); + } + + void Implements(char* List) + { + List[I_OnRehash] = List[I_OnPreCommand] = List[I_OnCleanup] = 1; + } + + virtual ~ModuleBlockAmsg() + { + } + + virtual Version GetVersion() + { + return Version(1,1,0,0,VF_VENDOR,API_VERSION); + } + + virtual void OnRehash(userrec* user, const std::string ¶meter) + { + ConfigReader Conf(ServerInstance); + + ForgetDelay = Conf.ReadInteger("blockamsg", "delay", 0, false); + + if(Conf.GetError() == CONF_VALUE_NOT_FOUND) + ForgetDelay = -1; + + std::string act = Conf.ReadValue("blockamsg", "action", 0); + + if(act == "notice") + action = IBLOCK_NOTICE; + else if(act == "noticeopers") + action = IBLOCK_NOTICEOPERS; + else if(act == "silent") + action = IBLOCK_SILENT; + else if(act == "kill") + action = IBLOCK_KILL; + else + action = IBLOCK_KILLOPERS; + } + + virtual int OnPreCommand(const std::string &command, const char** parameters, int pcnt, userrec *user, bool validated, const std::string &original_line) + { + // Don't do anything with unregistered users, or remote ones. + if(!user || (user->registered != REG_ALL) || !IS_LOCAL(user)) + return 0; + + // We want case insensitive command comparison. + // Add std::string contructor for irc::string :x + irc::string cmd = command.c_str(); + + if(validated && (cmd == "PRIVMSG" || cmd == "NOTICE") && (pcnt >= 2)) + { + // parameters[0] should have the target(s) in it. + // I think it will be faster to first check if there are any commas, and if there are then try and parse it out. + // Most messages have a single target so... + + int targets = 1; + int userchans = 0; + + if(*parameters[0] != '#') + { + // Decrement if the first target wasn't a channel. + targets--; + } + + for(const char* c = parameters[0]; *c; c++) + if((*c == ',') && *(c+1) && (*(c+1) == '#')) + targets++; + + /* targets should now contain the number of channel targets the msg/notice was pointed at. + * If the msg/notice was a PM there should be no channel targets and 'targets' should = 0. + * We don't want to block PMs so... + */ + if(targets == 0) + { + return 0; + } + + userchans = user->chans.size(); + + // Check that this message wasn't already sent within a few seconds. + BlockedMessage* m; + user->GetExt("amsgblock", m); + + // If the message is identical and within the time. + // We check the target is *not* identical, that'd straying into the realms of flood control. Which isn't what we're doing... + // OR + // The number of target channels is equal to the number of channels the sender is on..a little suspicious. + // Check it's more than 1 too, or else users on one channel would have fun. + if((m && (m->message == parameters[1]) && (m->target != parameters[0]) && (ForgetDelay != -1) && (m->sent >= ServerInstance->Time()-ForgetDelay)) || ((targets > 1) && (targets == userchans))) + { + // Block it... + if(action == IBLOCK_KILLOPERS || action == IBLOCK_NOTICEOPERS) + ServerInstance->WriteOpers("*** %s had an /amsg or /ame denied", user->nick); + + if(action == IBLOCK_KILL || action == IBLOCK_KILLOPERS) + userrec::QuitUser(ServerInstance, user, "Global message (/amsg or /ame) detected"); + else if(action == IBLOCK_NOTICE || action == IBLOCK_NOTICEOPERS) + user->WriteServ( "NOTICE %s :Global message (/amsg or /ame) detected", user->nick); + + return 1; + } + + if(m) + { + // If there's already a BlockedMessage allocated, use it. + m->message = parameters[1]; + m->target = parameters[0]; + m->sent = ServerInstance->Time(); + } + else + { + m = new BlockedMessage(parameters[1], parameters[0], ServerInstance->Time()); + user->Extend("amsgblock", (char*)m); + } + } + return 0; + } + + void OnCleanup(int target_type, void* item) + { + if(target_type == TYPE_USER) + { + userrec* user = (userrec*)item; + BlockedMessage* m; + user->GetExt("amsgblock", m); + if(m) + { + DELETE(m); + user->Shrink("amsgblock"); + } + } + } +}; + + +MODULE_INIT(ModuleBlockAmsg) diff --git a/src/modules/m_blockcaps.cpp b/src/modules/m_blockcaps.cpp index 8713f8c0d..9197a8f11 100644 --- a/src/modules/m_blockcaps.cpp +++ b/src/modules/m_blockcaps.cpp @@ -1 +1,143 @@ -/* +------------------------------------+
* | 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 "users.h"
#include "channels.h"
#include "modules.h"
#include "mode.h"
/* $ModDesc: Provides support for channel mode +P to block all-CAPS channel messages and notices */
/** Handles the +P channel mode
*/
class BlockCaps : public ModeHandler
{
public:
BlockCaps(InspIRCd* Instance) : ModeHandler(Instance, 'P', 0, 0, false, MODETYPE_CHANNEL, false) { }
ModeAction OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string ¶meter, bool adding)
{
if (adding)
{
if (!channel->IsModeSet('P'))
{
channel->SetMode('P',true);
return MODEACTION_ALLOW;
}
}
else
{
if (channel->IsModeSet('P'))
{
channel->SetMode('P',false);
return MODEACTION_ALLOW;
}
}
return MODEACTION_DENY;
}
};
class ModuleBlockCAPS : public Module
{
BlockCaps* bc;
int percent;
unsigned int minlen;
char capsmap[256];
public:
ModuleBlockCAPS(InspIRCd* Me) : Module(Me)
{
OnRehash(NULL,"");
bc = new BlockCaps(ServerInstance);
if (!ServerInstance->AddMode(bc, 'P'))
throw ModuleException("Could not add new modes!");
}
void Implements(char* List)
{
List[I_OnUserPreMessage] = List[I_OnUserPreNotice] = List[I_OnRehash] = 1;
}
virtual void OnRehash(userrec* user, const std::string ¶m)
{
ReadConf();
}
virtual int OnUserPreMessage(userrec* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list)
{
if (target_type == TYPE_CHANNEL)
{
if ((!IS_LOCAL(user)) || (text.length() < minlen))
return 0;
chanrec* c = (chanrec*)dest;
if (c->IsModeSet('P'))
{
int caps = 0;
for (std::string::iterator i = text.begin(); i != text.end(); i++)
caps += capsmap[(unsigned char)*i];
if ( ((caps*100)/(int)text.length()) >= percent )
{
user->WriteServ( "404 %s %s :Your line cannot be more than %d%% capital letters if it is %d or more letters long", user->nick, c->name, percent, minlen);
return 1;
}
}
}
return 0;
}
virtual int OnUserPreNotice(userrec* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list)
{
return OnUserPreMessage(user,dest,target_type,text,status,exempt_list);
}
void ReadConf()
{
ConfigReader Conf(ServerInstance);
percent = Conf.ReadInteger("blockcaps", "percent", "100", 0, true);
minlen = Conf.ReadInteger("blockcaps", "minlen", "0", 0, true);
std::string hmap = Conf.ReadValue("blockcaps", "capsmap", 0);
if (hmap.empty())
hmap = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
memset(&capsmap, 0, 255);
for (std::string::iterator n = hmap.begin(); n != hmap.end(); n++)
capsmap[(unsigned char)*n] = 1;
if (percent < 0 || percent > 100)
{
ServerInstance->Log(DEFAULT, "<blockcaps:percent> out of range, setting to default of 100.");
percent = 100;
}
if (minlen < 0 || minlen > MAXBUF-1)
{
ServerInstance->Log(DEFAULT, "<blockcaps:minlen> out of range, setting to default of 0.");
minlen = 0;
}
}
virtual ~ModuleBlockCAPS()
{
ServerInstance->Modes->DelMode(bc);
DELETE(bc);
}
virtual Version GetVersion()
{
return Version(1,1,0,0,VF_COMMON|VF_VENDOR,API_VERSION);
}
};
MODULE_INIT(ModuleBlockCAPS)
\ No newline at end of file +/* +------------------------------------+ + * | 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 "users.h" +#include "channels.h" +#include "modules.h" +#include "mode.h" + +/* $ModDesc: Provides support for channel mode +P to block all-CAPS channel messages and notices */ + + +/** Handles the +P channel mode + */ +class BlockCaps : public ModeHandler +{ + public: + BlockCaps(InspIRCd* Instance) : ModeHandler(Instance, 'P', 0, 0, false, MODETYPE_CHANNEL, false) { } + + ModeAction OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string ¶meter, bool adding) + { + if (adding) + { + if (!channel->IsModeSet('P')) + { + channel->SetMode('P',true); + return MODEACTION_ALLOW; + } + } + else + { + if (channel->IsModeSet('P')) + { + channel->SetMode('P',false); + return MODEACTION_ALLOW; + } + } + + return MODEACTION_DENY; + } +}; + +class ModuleBlockCAPS : public Module +{ + BlockCaps* bc; + int percent; + unsigned int minlen; + char capsmap[256]; +public: + + ModuleBlockCAPS(InspIRCd* Me) : Module(Me) + { + OnRehash(NULL,""); + bc = new BlockCaps(ServerInstance); + if (!ServerInstance->AddMode(bc, 'P')) + throw ModuleException("Could not add new modes!"); + } + + void Implements(char* List) + { + List[I_OnUserPreMessage] = List[I_OnUserPreNotice] = List[I_OnRehash] = 1; + } + + virtual void OnRehash(userrec* user, const std::string ¶m) + { + ReadConf(); + } + + virtual int OnUserPreMessage(userrec* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list) + { + if (target_type == TYPE_CHANNEL) + { + if ((!IS_LOCAL(user)) || (text.length() < minlen)) + return 0; + + chanrec* c = (chanrec*)dest; + + if (c->IsModeSet('P')) + { + int caps = 0; + for (std::string::iterator i = text.begin(); i != text.end(); i++) + caps += capsmap[(unsigned char)*i]; + if ( ((caps*100)/(int)text.length()) >= percent ) + { + user->WriteServ( "404 %s %s :Your line cannot be more than %d%% capital letters if it is %d or more letters long", user->nick, c->name, percent, minlen); + return 1; + } + } + } + return 0; + } + + virtual int OnUserPreNotice(userrec* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list) + { + return OnUserPreMessage(user,dest,target_type,text,status,exempt_list); + } + + void ReadConf() + { + ConfigReader Conf(ServerInstance); + percent = Conf.ReadInteger("blockcaps", "percent", "100", 0, true); + minlen = Conf.ReadInteger("blockcaps", "minlen", "0", 0, true); + std::string hmap = Conf.ReadValue("blockcaps", "capsmap", 0); + if (hmap.empty()) + hmap = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + memset(&capsmap, 0, 255); + for (std::string::iterator n = hmap.begin(); n != hmap.end(); n++) + capsmap[(unsigned char)*n] = 1; + if (percent < 0 || percent > 100) + { + ServerInstance->Log(DEFAULT, "<blockcaps:percent> out of range, setting to default of 100."); + percent = 100; + } + if (minlen < 0 || minlen > MAXBUF-1) + { + ServerInstance->Log(DEFAULT, "<blockcaps:minlen> out of range, setting to default of 0."); + minlen = 0; + } + } + + virtual ~ModuleBlockCAPS() + { + ServerInstance->Modes->DelMode(bc); + DELETE(bc); + } + + virtual Version GetVersion() + { + return Version(1,1,0,0,VF_COMMON|VF_VENDOR,API_VERSION); + } +}; + +MODULE_INIT(ModuleBlockCAPS) diff --git a/src/modules/m_blockcolor.cpp b/src/modules/m_blockcolor.cpp index 0646caa0b..69b0e4686 100644 --- a/src/modules/m_blockcolor.cpp +++ b/src/modules/m_blockcolor.cpp @@ -1 +1,118 @@ -/* +------------------------------------+
* | 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 "users.h"
#include "channels.h"
#include "modules.h"
/* $ModDesc: Provides support for unreal-style channel mode +c */
/** Handles the +c channel mode
*/
class BlockColor : public ModeHandler
{
public:
BlockColor(InspIRCd* Instance) : ModeHandler(Instance, 'c', 0, 0, false, MODETYPE_CHANNEL, false) { }
ModeAction OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string ¶meter, bool adding)
{
if (adding)
{
if (!channel->IsModeSet('c'))
{
channel->SetMode('c',true);
return MODEACTION_ALLOW;
}
}
else
{
if (channel->IsModeSet('c'))
{
channel->SetMode('c',false);
return MODEACTION_ALLOW;
}
}
return MODEACTION_DENY;
}
};
class ModuleBlockColour : public Module
{
bool AllowChanOps;
BlockColor *bc;
public:
ModuleBlockColour(InspIRCd* Me) : Module(Me)
{
bc = new BlockColor(ServerInstance);
if (!ServerInstance->AddMode(bc, 'c'))
throw ModuleException("Could not add new modes!");
}
void Implements(char* List)
{
List[I_OnUserPreMessage] = List[I_OnUserPreNotice] = 1;
}
virtual int OnUserPreMessage(userrec* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list)
{
if ((target_type == TYPE_CHANNEL) && (IS_LOCAL(user)))
{
chanrec* c = (chanrec*)dest;
if(c->IsModeSet('c'))
{
if (!CHANOPS_EXEMPT(ServerInstance, 'c') || CHANOPS_EXEMPT(ServerInstance, 'c') && c->GetStatus(user) != STATUS_OP)
{
for (std::string::iterator i = text.begin(); i != text.end(); i++)
{
switch (*i)
{
case 2:
case 3:
case 15:
case 21:
case 22:
case 31:
user->WriteServ("404 %s %s :Can't send colours to channel (+c set)",user->nick, c->name);
return 1;
break;
}
}
}
}
}
return 0;
}
virtual int OnUserPreNotice(userrec* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list)
{
return OnUserPreMessage(user,dest,target_type,text,status,exempt_list);
}
virtual ~ModuleBlockColour()
{
ServerInstance->Modes->DelMode(bc);
DELETE(bc);
}
virtual Version GetVersion()
{
return Version(1,1,0,0,VF_COMMON|VF_VENDOR,API_VERSION);
}
};
MODULE_INIT(ModuleBlockColour)
\ No newline at end of file +/* +------------------------------------+ + * | 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 "users.h" +#include "channels.h" +#include "modules.h" + +/* $ModDesc: Provides support for unreal-style channel mode +c */ + +/** Handles the +c channel mode + */ +class BlockColor : public ModeHandler +{ + public: + BlockColor(InspIRCd* Instance) : ModeHandler(Instance, 'c', 0, 0, false, MODETYPE_CHANNEL, false) { } + + ModeAction OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string ¶meter, bool adding) + { + if (adding) + { + if (!channel->IsModeSet('c')) + { + channel->SetMode('c',true); + return MODEACTION_ALLOW; + } + } + else + { + if (channel->IsModeSet('c')) + { + channel->SetMode('c',false); + return MODEACTION_ALLOW; + } + } + + return MODEACTION_DENY; + } +}; + +class ModuleBlockColour : public Module +{ + bool AllowChanOps; + BlockColor *bc; + public: + + ModuleBlockColour(InspIRCd* Me) : Module(Me) + { + bc = new BlockColor(ServerInstance); + if (!ServerInstance->AddMode(bc, 'c')) + throw ModuleException("Could not add new modes!"); + } + + void Implements(char* List) + { + List[I_OnUserPreMessage] = List[I_OnUserPreNotice] = 1; + } + + + virtual int OnUserPreMessage(userrec* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list) + { + if ((target_type == TYPE_CHANNEL) && (IS_LOCAL(user))) + { + chanrec* c = (chanrec*)dest; + + if(c->IsModeSet('c')) + { + if (!CHANOPS_EXEMPT(ServerInstance, 'c') || CHANOPS_EXEMPT(ServerInstance, 'c') && c->GetStatus(user) != STATUS_OP) + { + for (std::string::iterator i = text.begin(); i != text.end(); i++) + { + switch (*i) + { + case 2: + case 3: + case 15: + case 21: + case 22: + case 31: + user->WriteServ("404 %s %s :Can't send colours to channel (+c set)",user->nick, c->name); + return 1; + break; + } + } + } + } + } + return 0; + } + + virtual int OnUserPreNotice(userrec* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list) + { + return OnUserPreMessage(user,dest,target_type,text,status,exempt_list); + } + + virtual ~ModuleBlockColour() + { + ServerInstance->Modes->DelMode(bc); + DELETE(bc); + } + + virtual Version GetVersion() + { + return Version(1,1,0,0,VF_COMMON|VF_VENDOR,API_VERSION); + } +}; + +MODULE_INIT(ModuleBlockColour) diff --git a/src/modules/m_botmode.cpp b/src/modules/m_botmode.cpp index 8cc999f12..a6cad9577 100644 --- a/src/modules/m_botmode.cpp +++ b/src/modules/m_botmode.cpp @@ -1 +1,95 @@ -/* +------------------------------------+
* | 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 <stdio.h>
#include <string>
#include "users.h"
#include "channels.h"
#include "modules.h"
#include "configreader.h"
/* $ModDesc: Provides support for unreal-style umode +B */
/** Handles user mode +B
*/
class BotMode : public ModeHandler
{
public:
BotMode(InspIRCd* Instance) : ModeHandler(Instance, 'B', 0, 0, false, MODETYPE_USER, false) { }
ModeAction OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string ¶meter, bool adding)
{
if (adding)
{
if (!dest->IsModeSet('B'))
{
dest->SetMode('B',true);
return MODEACTION_ALLOW;
}
}
else
{
if (dest->IsModeSet('B'))
{
dest->SetMode('B',false);
return MODEACTION_ALLOW;
}
}
return MODEACTION_DENY;
}
};
class ModuleBotMode : public Module
{
BotMode* bm;
public:
ModuleBotMode(InspIRCd* Me)
: Module(Me)
{
bm = new BotMode(ServerInstance);
if (!ServerInstance->AddMode(bm, 'B'))
throw ModuleException("Could not add new modes!");
}
void Implements(char* List)
{
List[I_OnWhois] = 1;
}
virtual ~ModuleBotMode()
{
ServerInstance->Modes->DelMode(bm);
DELETE(bm);
}
virtual Version GetVersion()
{
return Version(1,1,0,0,VF_COMMON|VF_VENDOR,API_VERSION);
}
virtual void OnWhois(userrec* src, userrec* dst)
{
if (dst->IsModeSet('B'))
{
ServerInstance->SendWhoisLine(src, dst, 335, std::string(src->nick)+" "+std::string(dst->nick)+" :is a bot on "+ServerInstance->Config->Network);
}
}
};
MODULE_INIT(ModuleBotMode)
\ No newline at end of file +/* +------------------------------------+ + * | 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 <stdio.h> +#include <string> +#include "users.h" +#include "channels.h" +#include "modules.h" +#include "configreader.h" + +/* $ModDesc: Provides support for unreal-style umode +B */ + +/** Handles user mode +B + */ +class BotMode : public ModeHandler +{ + public: + BotMode(InspIRCd* Instance) : ModeHandler(Instance, 'B', 0, 0, false, MODETYPE_USER, false) { } + + ModeAction OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string ¶meter, bool adding) + { + if (adding) + { + if (!dest->IsModeSet('B')) + { + dest->SetMode('B',true); + return MODEACTION_ALLOW; + } + } + else + { + if (dest->IsModeSet('B')) + { + dest->SetMode('B',false); + return MODEACTION_ALLOW; + } + } + + return MODEACTION_DENY; + } +}; + +class ModuleBotMode : public Module +{ + + BotMode* bm; + public: + ModuleBotMode(InspIRCd* Me) + : Module(Me) + { + + bm = new BotMode(ServerInstance); + if (!ServerInstance->AddMode(bm, 'B')) + throw ModuleException("Could not add new modes!"); + } + + void Implements(char* List) + { + List[I_OnWhois] = 1; + } + + virtual ~ModuleBotMode() + { + ServerInstance->Modes->DelMode(bm); + DELETE(bm); + } + + virtual Version GetVersion() + { + return Version(1,1,0,0,VF_COMMON|VF_VENDOR,API_VERSION); + } + + virtual void OnWhois(userrec* src, userrec* dst) + { + if (dst->IsModeSet('B')) + { + ServerInstance->SendWhoisLine(src, dst, 335, std::string(src->nick)+" "+std::string(dst->nick)+" :is a bot on "+ServerInstance->Config->Network); + } + } + +}; + + +MODULE_INIT(ModuleBotMode) diff --git a/src/modules/m_cban.cpp b/src/modules/m_cban.cpp index 65be0d9bb..c8e6a86b9 100644 --- a/src/modules/m_cban.cpp +++ b/src/modules/m_cban.cpp @@ -1 +1,251 @@ -/* +------------------------------------+
* | 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 <algorithm>
#include "users.h"
#include "channels.h"
#include "modules.h"
#include "configreader.h"
/* $ModDesc: Gives /cban, aka C:lines. Think Q:lines, for channels. */
/** Holds a CBAN item
*/
class CBan : public classbase
{
public:
irc::string chname;
std::string set_by;
time_t set_on;
long length;
std::string reason;
CBan()
{
}
CBan(irc::string cn, std::string sb, time_t so, long ln, std::string rs) : chname(cn), set_by(sb), set_on(so), length(ln), reason(rs)
{
}
};
bool CBanComp(const CBan &ban1, const CBan &ban2);
typedef std::vector<CBan> cbanlist;
/* cbans is declared here, as our type is right above. Don't try move it. */
cbanlist cbans;
/** Handle /CBAN
*/
class cmd_cban : public command_t
{
public:
cmd_cban(InspIRCd* Me) : command_t(Me, "CBAN", 'o', 1)
{
this->source = "m_cban.so";
this->syntax = "<channel> [<duration> :<reason>]";
}
CmdResult Handle(const char** parameters, int pcnt, userrec *user)
{
/* syntax: CBAN #channel time :reason goes here */
/* 'time' is a human-readable timestring, like 2d3h2s. */
if(pcnt == 1)
{
/* form: CBAN #channel removes a CBAN */
for (cbanlist::iterator iter = cbans.begin(); iter != cbans.end(); iter++)
{
if (parameters[0] == iter->chname)
{
long remaining = iter->length + ServerInstance->Time();
user->WriteServ("386 %s %s :Removed CBAN due to expire at %s (%s)", user->nick, iter->chname.c_str(), ServerInstance->TimeString(remaining).c_str(), iter->reason.c_str());
cbans.erase(iter);
break;
}
}
}
else if (pcnt >= 2)
{
/* full form to add a CBAN */
if (ServerInstance->IsChannel(parameters[0]))
{
// parameters[0] = #channel
// parameters[1] = 1h3m2s
// parameters[2] = Tortoise abuser
long length = ServerInstance->Duration(parameters[1]);
std::string reason = (pcnt > 2) ? parameters[2] : "No reason supplied";
cbans.push_back(CBan(parameters[0], user->nick, ServerInstance->Time(), length, reason));
std::sort(cbans.begin(), cbans.end(), CBanComp);
if(length > 0)
{
user->WriteServ("385 %s %s :Added %lu second channel ban (%s)", user->nick, parameters[0], length, reason.c_str());
ServerInstance->WriteOpers("*** %s added %lu second channel ban on %s (%s)", user->nick, length, parameters[0], reason.c_str());
}
else
{
user->WriteServ("385 %s %s :Added permanent channel ban (%s)", user->nick, parameters[0], reason.c_str());
ServerInstance->WriteOpers("*** %s added permanent channel ban on %s (%s)", user->nick, parameters[0], reason.c_str());
}
}
else
{
user->WriteServ("403 %s %s :Invalid channel name", user->nick, parameters[0]);
return CMD_FAILURE;
}
}
/* we want this routed! */
return CMD_SUCCESS;
}
};
bool CBanComp(const CBan &ban1, const CBan &ban2)
{
return ((ban1.set_on + ban1.length) < (ban2.set_on + ban2.length));
}
class ModuleCBan : public Module
{
cmd_cban* mycommand;
public:
ModuleCBan(InspIRCd* Me) : Module(Me)
{
mycommand = new cmd_cban(Me);
ServerInstance->AddCommand(mycommand);
}
void Implements(char* List)
{
List[I_OnUserPreJoin] = List[I_OnSyncOtherMetaData] = List[I_OnDecodeMetaData] = List[I_OnStats] = 1;
}
virtual int OnStats(char symbol, userrec* user, string_list &results)
{
ExpireBans();
if(symbol == 'C')
{
for(cbanlist::iterator iter = cbans.begin(); iter != cbans.end(); iter++)
{
unsigned long remaining = (iter->set_on + iter->length) - ServerInstance->Time();
results.push_back(std::string(ServerInstance->Config->ServerName)+" 210 "+user->nick+" "+iter->chname.c_str()+" "+iter->set_by+" "+ConvToStr(iter->set_on)+" "+ConvToStr(iter->length)+" "+ConvToStr(remaining)+" :"+iter->reason);
}
}
return 0;
}
virtual int OnUserPreJoin(userrec *user, chanrec *chan, const char *cname, std::string &privs)
{
ExpireBans();
/* check cbans in here, and apply as necessary. */
for(cbanlist::iterator iter = cbans.begin(); iter != cbans.end(); iter++)
{
if(iter->chname == cname && !user->modes[UM_OPERATOR])
{
// Channel is banned.
user->WriteServ( "384 %s %s :Cannot join channel, CBANed (%s)", user->nick, cname, iter->reason.c_str());
ServerInstance->WriteOpers("*** %s tried to join %s which is CBANed (%s)", user->nick, cname, iter->reason.c_str());
return 1;
}
}
return 0;
}
virtual void OnSyncOtherMetaData(Module* proto, void* opaque, bool displayable)
{
for(cbanlist::iterator iter = cbans.begin(); iter != cbans.end(); iter++)
{
proto->ProtoSendMetaData(opaque, TYPE_OTHER, NULL, "cban", EncodeCBan(*iter));
}
}
virtual void OnDecodeMetaData(int target_type, void* target, const std::string &extname, const std::string &extdata)
{
if((target_type == TYPE_OTHER) && (extname == "cban"))
{
cbans.push_back(DecodeCBan(extdata));
std::sort(cbans.begin(), cbans.end(), CBanComp);
}
}
virtual ~ModuleCBan()
{
}
virtual Version GetVersion()
{
return Version(1,1,0,1,VF_VENDOR,API_VERSION);
}
std::string EncodeCBan(const CBan &ban)
{
std::ostringstream stream;
stream << ban.chname << " " << ban.set_by << " " << ban.set_on << " " << ban.length << " :" << ban.reason;
return stream.str();
}
CBan DecodeCBan(const std::string &data)
{
CBan res;
int set_on;
irc::tokenstream tokens(data);
tokens.GetToken(res.chname);
tokens.GetToken(res.set_by);
tokens.GetToken(set_on);
res.set_on = set_on;
tokens.GetToken(res.length);
tokens.GetToken(res.reason);
return res;
}
void ExpireBans()
{
bool go_again = true;
while (go_again)
{
go_again = false;
for (cbanlist::iterator iter = cbans.begin(); iter != cbans.end(); iter++)
{
/* 0 == permanent, don't mess with them! -- w00t */
if (iter->length != 0)
{
if (iter->set_on + iter->length <= ServerInstance->Time())
{
ServerInstance->WriteOpers("*** %li second CBAN on %s (%s) set on %s expired", iter->length, iter->chname.c_str(), iter->reason.c_str(), ServerInstance->TimeString(iter->set_on).c_str());
cbans.erase(iter);
go_again = true;
}
}
if (go_again == true)
break;
}
}
}
};
MODULE_INIT(ModuleCBan)
\ No newline at end of file +/* +------------------------------------+ + * | 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 <algorithm> +#include "users.h" +#include "channels.h" +#include "modules.h" +#include "configreader.h" + +/* $ModDesc: Gives /cban, aka C:lines. Think Q:lines, for channels. */ + +/** Holds a CBAN item + */ +class CBan : public classbase +{ +public: + irc::string chname; + std::string set_by; + time_t set_on; + long length; + std::string reason; + + CBan() + { + } + + CBan(irc::string cn, std::string sb, time_t so, long ln, std::string rs) : chname(cn), set_by(sb), set_on(so), length(ln), reason(rs) + { + } +}; + +bool CBanComp(const CBan &ban1, const CBan &ban2); + +typedef std::vector<CBan> cbanlist; + +/* cbans is declared here, as our type is right above. Don't try move it. */ +cbanlist cbans; + +/** Handle /CBAN + */ +class cmd_cban : public command_t +{ + public: + cmd_cban(InspIRCd* Me) : command_t(Me, "CBAN", 'o', 1) + { + this->source = "m_cban.so"; + this->syntax = "<channel> [<duration> :<reason>]"; + } + + CmdResult Handle(const char** parameters, int pcnt, userrec *user) + { + /* syntax: CBAN #channel time :reason goes here */ + /* 'time' is a human-readable timestring, like 2d3h2s. */ + + if(pcnt == 1) + { + /* form: CBAN #channel removes a CBAN */ + for (cbanlist::iterator iter = cbans.begin(); iter != cbans.end(); iter++) + { + if (parameters[0] == iter->chname) + { + long remaining = iter->length + ServerInstance->Time(); + user->WriteServ("386 %s %s :Removed CBAN due to expire at %s (%s)", user->nick, iter->chname.c_str(), ServerInstance->TimeString(remaining).c_str(), iter->reason.c_str()); + cbans.erase(iter); + break; + } + } + } + else if (pcnt >= 2) + { + /* full form to add a CBAN */ + if (ServerInstance->IsChannel(parameters[0])) + { + // parameters[0] = #channel + // parameters[1] = 1h3m2s + // parameters[2] = Tortoise abuser + long length = ServerInstance->Duration(parameters[1]); + std::string reason = (pcnt > 2) ? parameters[2] : "No reason supplied"; + + cbans.push_back(CBan(parameters[0], user->nick, ServerInstance->Time(), length, reason)); + + std::sort(cbans.begin(), cbans.end(), CBanComp); + + if(length > 0) + { + user->WriteServ("385 %s %s :Added %lu second channel ban (%s)", user->nick, parameters[0], length, reason.c_str()); + ServerInstance->WriteOpers("*** %s added %lu second channel ban on %s (%s)", user->nick, length, parameters[0], reason.c_str()); + } + else + { + user->WriteServ("385 %s %s :Added permanent channel ban (%s)", user->nick, parameters[0], reason.c_str()); + ServerInstance->WriteOpers("*** %s added permanent channel ban on %s (%s)", user->nick, parameters[0], reason.c_str()); + } + } + else + { + user->WriteServ("403 %s %s :Invalid channel name", user->nick, parameters[0]); + return CMD_FAILURE; + } + } + + /* we want this routed! */ + return CMD_SUCCESS; + } +}; + +bool CBanComp(const CBan &ban1, const CBan &ban2) +{ + return ((ban1.set_on + ban1.length) < (ban2.set_on + ban2.length)); +} + +class ModuleCBan : public Module +{ + cmd_cban* mycommand; + + + public: + ModuleCBan(InspIRCd* Me) : Module(Me) + { + + mycommand = new cmd_cban(Me); + ServerInstance->AddCommand(mycommand); + } + + void Implements(char* List) + { + List[I_OnUserPreJoin] = List[I_OnSyncOtherMetaData] = List[I_OnDecodeMetaData] = List[I_OnStats] = 1; + } + + virtual int OnStats(char symbol, userrec* user, string_list &results) + { + ExpireBans(); + + if(symbol == 'C') + { + for(cbanlist::iterator iter = cbans.begin(); iter != cbans.end(); iter++) + { + unsigned long remaining = (iter->set_on + iter->length) - ServerInstance->Time(); + results.push_back(std::string(ServerInstance->Config->ServerName)+" 210 "+user->nick+" "+iter->chname.c_str()+" "+iter->set_by+" "+ConvToStr(iter->set_on)+" "+ConvToStr(iter->length)+" "+ConvToStr(remaining)+" :"+iter->reason); + } + } + + return 0; + } + + virtual int OnUserPreJoin(userrec *user, chanrec *chan, const char *cname, std::string &privs) + { + ExpireBans(); + + /* check cbans in here, and apply as necessary. */ + for(cbanlist::iterator iter = cbans.begin(); iter != cbans.end(); iter++) + { + if(iter->chname == cname && !user->modes[UM_OPERATOR]) + { + // Channel is banned. + user->WriteServ( "384 %s %s :Cannot join channel, CBANed (%s)", user->nick, cname, iter->reason.c_str()); + ServerInstance->WriteOpers("*** %s tried to join %s which is CBANed (%s)", user->nick, cname, iter->reason.c_str()); + return 1; + } + } + return 0; + } + + virtual void OnSyncOtherMetaData(Module* proto, void* opaque, bool displayable) + { + for(cbanlist::iterator iter = cbans.begin(); iter != cbans.end(); iter++) + { + proto->ProtoSendMetaData(opaque, TYPE_OTHER, NULL, "cban", EncodeCBan(*iter)); + } + } + + virtual void OnDecodeMetaData(int target_type, void* target, const std::string &extname, const std::string &extdata) + { + if((target_type == TYPE_OTHER) && (extname == "cban")) + { + cbans.push_back(DecodeCBan(extdata)); + std::sort(cbans.begin(), cbans.end(), CBanComp); + } + } + + virtual ~ModuleCBan() + { + } + + virtual Version GetVersion() + { + return Version(1,1,0,1,VF_VENDOR,API_VERSION); + } + + std::string EncodeCBan(const CBan &ban) + { + std::ostringstream stream; + stream << ban.chname << " " << ban.set_by << " " << ban.set_on << " " << ban.length << " :" << ban.reason; + return stream.str(); + } + + CBan DecodeCBan(const std::string &data) + { + CBan res; + int set_on; + irc::tokenstream tokens(data); + tokens.GetToken(res.chname); + tokens.GetToken(res.set_by); + tokens.GetToken(set_on); + res.set_on = set_on; + tokens.GetToken(res.length); + tokens.GetToken(res.reason); + return res; + } + + void ExpireBans() + { + bool go_again = true; + + while (go_again) + { + go_again = false; + + for (cbanlist::iterator iter = cbans.begin(); iter != cbans.end(); iter++) + { + /* 0 == permanent, don't mess with them! -- w00t */ + if (iter->length != 0) + { + if (iter->set_on + iter->length <= ServerInstance->Time()) + { + ServerInstance->WriteOpers("*** %li second CBAN on %s (%s) set on %s expired", iter->length, iter->chname.c_str(), iter->reason.c_str(), ServerInstance->TimeString(iter->set_on).c_str()); + cbans.erase(iter); + go_again = true; + } + } + + if (go_again == true) + break; + } + } + } +}; + +MODULE_INIT(ModuleCBan) + diff --git a/src/modules/m_censor.cpp b/src/modules/m_censor.cpp index a7aa2f8b1..f4a5bd620 100644 --- a/src/modules/m_censor.cpp +++ b/src/modules/m_censor.cpp @@ -1 +1,196 @@ -/* +------------------------------------+
* | 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.
*
* ---------------------------------------------------
*/
#define _CRT_SECURE_NO_DEPRECATE
#define _SCL_SECURE_NO_DEPRECATE
#include "inspircd.h"
#include "users.h"
#include "channels.h"
#include "modules.h"
typedef std::map<irc::string,irc::string> censor_t;
/* $ModDesc: Provides user and channel +G mode */
/** Handles usermode +G
*/
class CensorUser : public ModeHandler
{
public:
CensorUser(InspIRCd* Instance) : ModeHandler(Instance, 'G', 0, 0, false, MODETYPE_USER, false) { }
ModeAction OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string ¶meter, bool adding)
{
if (adding)
{
if (!dest->IsModeSet('G'))
{
dest->SetMode('G',true);
return MODEACTION_ALLOW;
}
}
else
{
if (dest->IsModeSet('G'))
{
dest->SetMode('G',false);
return MODEACTION_ALLOW;
}
}
return MODEACTION_DENY;
}
};
/** Handles channel mode +G
*/
class CensorChannel : public ModeHandler
{
public:
CensorChannel(InspIRCd* Instance) : ModeHandler(Instance, 'G', 0, 0, false, MODETYPE_CHANNEL, false) { }
ModeAction OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string ¶meter, bool adding)
{
if (adding)
{
if (!channel->IsModeSet('G'))
{
channel->SetMode('G',true);
return MODEACTION_ALLOW;
}
}
else
{
if (channel->IsModeSet('G'))
{
channel->SetMode('G',false);
return MODEACTION_ALLOW;
}
}
return MODEACTION_ALLOW;
}
};
class ModuleCensor : public Module
{
censor_t censors;
CensorUser *cu;
CensorChannel *cc;
public:
ModuleCensor(InspIRCd* Me)
: Module(Me)
{
/* Read the configuration file on startup.
*/
OnRehash(NULL,"");
cu = new CensorUser(ServerInstance);
cc = new CensorChannel(ServerInstance);
if (!ServerInstance->AddMode(cu, 'G') || !ServerInstance->AddMode(cc, 'G'))
throw ModuleException("Could not add new modes!");
}
void Implements(char* List)
{
List[I_OnRehash] = List[I_OnUserPreMessage] = List[I_OnUserPreNotice] = 1;
}
virtual ~ModuleCensor()
{
ServerInstance->Modes->DelMode(cu);
ServerInstance->Modes->DelMode(cc);
DELETE(cu);
DELETE(cc);
}
virtual void ReplaceLine(irc::string &text, irc::string pattern, irc::string replace)
{
if ((!pattern.empty()) && (!text.empty()))
{
std::string::size_type pos;
while ((pos = text.find(pattern)) != irc::string::npos)
{
text.erase(pos,pattern.length());
text.insert(pos,replace);
}
}
}
// format of a config entry is <badword text="shit" replace="poo">
virtual int OnUserPreMessage(userrec* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list)
{
if (!IS_LOCAL(user))
return 0;
bool active = false;
if (target_type == TYPE_USER)
active = ((userrec*)dest)->IsModeSet('G');
else if (target_type == TYPE_CHANNEL)
active = ((chanrec*)dest)->IsModeSet('G');
if (!active)
return 0;
irc::string text2 = text.c_str();
for (censor_t::iterator index = censors.begin(); index != censors.end(); index++)
{
if (text2.find(index->first) != irc::string::npos)
{
if (index->second.empty())
{
user->WriteServ("936 %s %s %s :Your message contained a censored word, and was blocked", user->nick, ((chanrec*)dest)->name, index->first.c_str());
return 1;
}
this->ReplaceLine(text2,index->first,index->second);
}
}
text = text2.c_str();
return 0;
}
virtual int OnUserPreNotice(userrec* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list)
{
return OnUserPreMessage(user,dest,target_type,text,status,exempt_list);
}
virtual void OnRehash(userrec* user, const std::string ¶meter)
{
/*
* reload our config file on rehash - we must destroy and re-allocate the classes
* to call the constructor again and re-read our data.
*/
ConfigReader* MyConf = new ConfigReader(ServerInstance);
censors.clear();
for (int index = 0; index < MyConf->Enumerate("badword"); index++)
{
irc::string pattern = (MyConf->ReadValue("badword","text",index)).c_str();
irc::string replace = (MyConf->ReadValue("badword","replace",index)).c_str();
censors[pattern] = replace;
}
DELETE(MyConf);
}
virtual Version GetVersion()
{
return Version(1,1,0,0,VF_COMMON|VF_VENDOR,API_VERSION);
}
};
MODULE_INIT(ModuleCensor)
\ No newline at end of file +/* +------------------------------------+ + * | 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. + * + * --------------------------------------------------- + */ + +#define _CRT_SECURE_NO_DEPRECATE +#define _SCL_SECURE_NO_DEPRECATE + +#include "inspircd.h" +#include "users.h" +#include "channels.h" +#include "modules.h" + +typedef std::map<irc::string,irc::string> censor_t; + +/* $ModDesc: Provides user and channel +G mode */ + +/** Handles usermode +G + */ +class CensorUser : public ModeHandler +{ + public: + CensorUser(InspIRCd* Instance) : ModeHandler(Instance, 'G', 0, 0, false, MODETYPE_USER, false) { } + + ModeAction OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string ¶meter, bool adding) + { + if (adding) + { + if (!dest->IsModeSet('G')) + { + dest->SetMode('G',true); + return MODEACTION_ALLOW; + } + } + else + { + if (dest->IsModeSet('G')) + { + dest->SetMode('G',false); + return MODEACTION_ALLOW; + } + } + + return MODEACTION_DENY; + } +}; + +/** Handles channel mode +G + */ +class CensorChannel : public ModeHandler +{ + public: + CensorChannel(InspIRCd* Instance) : ModeHandler(Instance, 'G', 0, 0, false, MODETYPE_CHANNEL, false) { } + + ModeAction OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string ¶meter, bool adding) + { + if (adding) + { + if (!channel->IsModeSet('G')) + { + channel->SetMode('G',true); + return MODEACTION_ALLOW; + } + } + else + { + if (channel->IsModeSet('G')) + { + channel->SetMode('G',false); + return MODEACTION_ALLOW; + } + } + + return MODEACTION_ALLOW; + } +}; + +class ModuleCensor : public Module +{ + + + censor_t censors; + CensorUser *cu; + CensorChannel *cc; + + public: + ModuleCensor(InspIRCd* Me) + : Module(Me) + { + /* Read the configuration file on startup. + */ + OnRehash(NULL,""); + cu = new CensorUser(ServerInstance); + cc = new CensorChannel(ServerInstance); + if (!ServerInstance->AddMode(cu, 'G') || !ServerInstance->AddMode(cc, 'G')) + throw ModuleException("Could not add new modes!"); + } + + void Implements(char* List) + { + List[I_OnRehash] = List[I_OnUserPreMessage] = List[I_OnUserPreNotice] = 1; + } + + virtual ~ModuleCensor() + { + ServerInstance->Modes->DelMode(cu); + ServerInstance->Modes->DelMode(cc); + DELETE(cu); + DELETE(cc); + } + + virtual void ReplaceLine(irc::string &text, irc::string pattern, irc::string replace) + { + if ((!pattern.empty()) && (!text.empty())) + { + std::string::size_type pos; + while ((pos = text.find(pattern)) != irc::string::npos) + { + text.erase(pos,pattern.length()); + text.insert(pos,replace); + } + } + } + + // format of a config entry is <badword text="shit" replace="poo"> + virtual int OnUserPreMessage(userrec* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list) + { + if (!IS_LOCAL(user)) + return 0; + + bool active = false; + + if (target_type == TYPE_USER) + active = ((userrec*)dest)->IsModeSet('G'); + else if (target_type == TYPE_CHANNEL) + active = ((chanrec*)dest)->IsModeSet('G'); + + if (!active) + return 0; + + irc::string text2 = text.c_str(); + for (censor_t::iterator index = censors.begin(); index != censors.end(); index++) + { + if (text2.find(index->first) != irc::string::npos) + { + if (index->second.empty()) + { + user->WriteServ("936 %s %s %s :Your message contained a censored word, and was blocked", user->nick, ((chanrec*)dest)->name, index->first.c_str()); + return 1; + } + + this->ReplaceLine(text2,index->first,index->second); + } + } + text = text2.c_str(); + return 0; + } + + virtual int OnUserPreNotice(userrec* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list) + { + return OnUserPreMessage(user,dest,target_type,text,status,exempt_list); + } + + virtual void OnRehash(userrec* user, const std::string ¶meter) + { + /* + * reload our config file on rehash - we must destroy and re-allocate the classes + * to call the constructor again and re-read our data. + */ + ConfigReader* MyConf = new ConfigReader(ServerInstance); + censors.clear(); + for (int index = 0; index < MyConf->Enumerate("badword"); index++) + { + irc::string pattern = (MyConf->ReadValue("badword","text",index)).c_str(); + irc::string replace = (MyConf->ReadValue("badword","replace",index)).c_str(); + censors[pattern] = replace; + } + DELETE(MyConf); + } + + virtual Version GetVersion() + { + return Version(1,1,0,0,VF_COMMON|VF_VENDOR,API_VERSION); + } + +}; + +MODULE_INIT(ModuleCensor) diff --git a/src/modules/m_cgiirc.cpp b/src/modules/m_cgiirc.cpp index 64fd6c69f..290f55d22 100644 --- a/src/modules/m_cgiirc.cpp +++ b/src/modules/m_cgiirc.cpp @@ -1 +1,511 @@ -/* +------------------------------------+
* | 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 "users.h"
#include "modules.h"
#include "dns.h"
#ifndef WINDOWS
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#endif
/* $ModDesc: Change user's hosts connecting from known CGI:IRC hosts */
enum CGItype { PASS, IDENT, PASSFIRST, IDENTFIRST, WEBIRC };
/** Holds a CGI site's details
*/
class CGIhost : public classbase
{
public:
std::string hostmask;
CGItype type;
std::string password;
CGIhost(const std::string &mask = "", CGItype t = IDENTFIRST, const std::string &password ="")
: hostmask(mask), type(t), password(password)
{
}
};
typedef std::vector<CGIhost> CGIHostlist;
class cmd_webirc : public command_t
{
InspIRCd* Me;
CGIHostlist Hosts;
bool notify;
public:
cmd_webirc(InspIRCd* Me, CGIHostlist &Hosts, bool notify) : command_t(Me, "WEBIRC", 0, 4, true), Hosts(Hosts), notify(notify)
{
this->source = "m_cgiirc.so";
this->syntax = "password client hostname ip";
}
CmdResult Handle(const char** parameters, int pcnt, userrec *user)
{
if(user->registered == REG_ALL)
return CMD_FAILURE;
for(CGIHostlist::iterator iter = Hosts.begin(); iter != Hosts.end(); iter++)
{
if(ServerInstance->MatchText(user->host, iter->hostmask) || ServerInstance->MatchText(user->GetIPString(), iter->hostmask))
{
if(iter->type == WEBIRC && parameters[0] == iter->password)
{
user->Extend("cgiirc_realhost", new std::string(user->host));
user->Extend("cgiirc_realip", new std::string(user->GetIPString()));
if (notify)
ServerInstance->WriteOpers("*** Connecting user %s detected as using CGI:IRC (%s), changing real host to %s from %s", user->nick, user->host, parameters[2], user->host);
user->Extend("cgiirc_webirc_hostname", new std::string(parameters[2]));
user->Extend("cgiirc_webirc_ip", new std::string(parameters[3]));
return CMD_LOCALONLY;
}
}
}
return CMD_FAILURE;
}
};
/** Resolver for CGI:IRC hostnames encoded in ident/GECOS
*/
class CGIResolver : public Resolver
{
std::string typ;
int theirfd;
userrec* them;
bool notify;
public:
CGIResolver(Module* me, InspIRCd* ServerInstance, bool NotifyOpers, const std::string &source, bool forward, userrec* u, int userfd, const std::string &type, bool &cached)
: Resolver(ServerInstance, source, forward ? DNS_QUERY_A : DNS_QUERY_PTR4, cached, me), typ(type), theirfd(userfd), them(u), notify(NotifyOpers) { }
virtual void OnLookupComplete(const std::string &result, unsigned int ttl, bool cached)
{
/* Check the user still exists */
if ((them) && (them == ServerInstance->SE->GetRef(theirfd)))
{
if (notify)
ServerInstance->WriteOpers("*** Connecting user %s detected as using CGI:IRC (%s), changing real host to %s from %s", them->nick, them->host, result.c_str(), typ.c_str());
strlcpy(them->host, result.c_str(), 63);
strlcpy(them->dhost, result.c_str(), 63);
strlcpy(them->ident, "~cgiirc", 8);
them->InvalidateCache();
}
}
virtual void OnError(ResolverError e, const std::string &errormessage)
{
if ((them) && (them == ServerInstance->SE->GetRef(theirfd)))
{
if (notify)
ServerInstance->WriteOpers("*** Connecting user %s detected as using CGI:IRC (%s), but their host can't be resolved from their %s!", them->nick, them->host,typ.c_str());
}
}
virtual ~CGIResolver()
{
}
};
class ModuleCgiIRC : public Module
{
cmd_webirc* mycommand;
bool NotifyOpers;
CGIHostlist Hosts;
public:
ModuleCgiIRC(InspIRCd* Me) : Module(Me)
{
OnRehash(NULL,"");
mycommand=new cmd_webirc(Me, Hosts, NotifyOpers);
ServerInstance->AddCommand(mycommand);
}
void Implements(char* List)
{
List[I_OnRehash] = List[I_OnUserRegister] = List[I_OnCleanup] = List[I_OnSyncUserMetaData] = List[I_OnDecodeMetaData] = List[I_OnUserQuit] = List[I_OnUserConnect] = 1;
}
virtual Priority Prioritize()
{
// We want to get here before m_cloaking and m_hostchange etc
return PRIORITY_FIRST;
}
virtual void OnRehash(userrec* user, const std::string ¶meter)
{
ConfigReader Conf(ServerInstance);
NotifyOpers = Conf.ReadFlag("cgiirc", "opernotice", 0); // If we send an oper notice when a CGI:IRC has their host changed.
if(Conf.GetError() == CONF_VALUE_NOT_FOUND)
NotifyOpers = true;
for(int i = 0; i < Conf.Enumerate("cgihost"); i++)
{
std::string hostmask = Conf.ReadValue("cgihost", "mask", i); // An allowed CGI:IRC host
std::string type = Conf.ReadValue("cgihost", "type", i); // What type of user-munging we do on this host.
std::string password = Conf.ReadValue("cgihost", "password", i);
if(hostmask.length())
{
if(type == "webirc" && !password.length()) {
ServerInstance->Log(DEFAULT, "m_cgiirc: Missing password in config: %s", hostmask.c_str());
} else {
CGItype cgitype;
if(type == "pass")
cgitype = PASS;
else if(type == "ident")
cgitype = IDENT;
else if(type == "passfirst")
cgitype = PASSFIRST;
else if(type == "webirc") {
cgitype = WEBIRC;
}
Hosts.push_back(CGIhost(hostmask,cgitype, password.length() ? password : "" ));
}
}
else
{
ServerInstance->Log(DEFAULT, "m_cgiirc.so: Invalid <cgihost:mask> value in config: %s", hostmask.c_str());
continue;
}
}
}
virtual void OnCleanup(int target_type, void* item)
{
if(target_type == TYPE_USER)
{
userrec* user = (userrec*)item;
std::string* realhost;
std::string* realip;
if(user->GetExt("cgiirc_realhost", realhost))
{
delete realhost;
user->Shrink("cgiirc_realhost");
}
if(user->GetExt("cgiirc_realip", realip))
{
delete realip;
user->Shrink("cgiirc_realip");
}
}
}
virtual void OnSyncUserMetaData(userrec* user, Module* proto, void* opaque, const std::string &extname, bool displayable)
{
if((extname == "cgiirc_realhost") || (extname == "cgiirc_realip"))
{
std::string* data;
if(user->GetExt(extname, data))
{
proto->ProtoSendMetaData(opaque, TYPE_USER, user, extname, *data);
}
}
}
virtual void OnDecodeMetaData(int target_type, void* target, const std::string &extname, const std::string &extdata)
{
if(target_type == TYPE_USER)
{
userrec* dest = (userrec*)target;
std::string* bleh;
if(((extname == "cgiirc_realhost") || (extname == "cgiirc_realip")) && (!dest->GetExt(extname, bleh)))
{
dest->Extend(extname, new std::string(extdata));
}
}
}
virtual void OnUserQuit(userrec* user, const std::string &message, const std::string &oper_message)
{
OnCleanup(TYPE_USER, user);
}
virtual int OnUserRegister(userrec* user)
{
for(CGIHostlist::iterator iter = Hosts.begin(); iter != Hosts.end(); iter++)
{
if(ServerInstance->MatchText(user->host, iter->hostmask) || ServerInstance->MatchText(user->GetIPString(), iter->hostmask))
{
// Deal with it...
if(iter->type == PASS)
{
CheckPass(user); // We do nothing if it fails so...
}
else if(iter->type == PASSFIRST && !CheckPass(user))
{
// If the password lookup failed, try the ident
CheckIdent(user); // If this fails too, do nothing
}
else if(iter->type == IDENT)
{
CheckIdent(user); // Nothing on failure.
}
else if(iter->type == IDENTFIRST && !CheckIdent(user))
{
// If the ident lookup fails, try the password.
CheckPass(user);
}
else if(iter->type == WEBIRC)
{
// We don't need to do anything here
}
return 0;
}
}
return 0;
}
virtual void OnUserConnect(userrec* user)
{
std::string *webirc_hostname, *webirc_ip;
if(user->GetExt("cgiirc_webirc_hostname", webirc_hostname))
{
strlcpy(user->host,webirc_hostname->c_str(),63);
strlcpy(user->dhost,webirc_hostname->c_str(),63);
delete webirc_hostname;
user->InvalidateCache();
user->Shrink("cgiirc_webirc_hostname");
}
if(user->GetExt("cgiirc_webirc_ip", webirc_ip))
{
bool valid=false;
user->RemoveCloneCounts();
#ifdef IPV6
valid = (inet_pton(AF_INET6, webirc_ip->c_str(), &((sockaddr_in6*)user->ip)->sin6_addr) > 0);
if(!valid)
valid = (inet_aton(webirc_ip->c_str(), &((sockaddr_in*)user->ip)->sin_addr));
#else
if (inet_aton(webirc_ip->c_str(), &((sockaddr_in*)user->ip)->sin_addr))
valid = true;
#endif
delete webirc_ip;
user->InvalidateCache();
user->Shrink("cgiirc_webirc_ip");
ServerInstance->AddLocalClone(user);
ServerInstance->AddGlobalClone(user);
user->CheckClass();
}
}
bool CheckPass(userrec* user)
{
if(IsValidHost(user->password))
{
user->Extend("cgiirc_realhost", new std::string(user->host));
user->Extend("cgiirc_realip", new std::string(user->GetIPString()));
strlcpy(user->host, user->password, 64);
strlcpy(user->dhost, user->password, 64);
user->InvalidateCache();
bool valid = false;
user->RemoveCloneCounts();
#ifdef IPV6
if (user->GetProtocolFamily() == AF_INET6)
valid = (inet_pton(AF_INET6, user->password, &((sockaddr_in6*)user->ip)->sin6_addr) > 0);
else
valid = (inet_aton(user->password, &((sockaddr_in*)user->ip)->sin_addr));
#else
if (inet_aton(user->password, &((sockaddr_in*)user->ip)->sin_addr))
valid = true;
#endif
ServerInstance->AddLocalClone(user);
ServerInstance->AddGlobalClone(user);
user->CheckClass();
if (valid)
{
/* We were given a IP in the password, we don't do DNS so they get this is as their host as well. */
if(NotifyOpers)
ServerInstance->WriteOpers("*** Connecting user %s detected as using CGI:IRC (%s), changing real host to %s from PASS", user->nick, user->host, user->password);
}
else
{
/* We got as resolved hostname in the password. */
try
{
bool cached;
CGIResolver* r = new CGIResolver(this, ServerInstance, NotifyOpers, user->password, false, user, user->GetFd(), "PASS", cached);
ServerInstance->AddResolver(r, cached);
}
catch (...)
{
if (NotifyOpers)
ServerInstance->WriteOpers("*** Connecting user %s detected as using CGI:IRC (%s), but i could not resolve their hostname!", user->nick, user->host);
}
}
*user->password = 0;
/*if(NotifyOpers)
ServerInstance->WriteOpers("*** Connecting user %s detected as using CGI:IRC (%s), changing real host to %s from PASS", user->nick, user->host, user->password);*/
return true;
}
return false;
}
bool CheckIdent(userrec* user)
{
int ip[4];
char* ident;
char newip[16];
int len = strlen(user->ident);
if(len == 8)
ident = user->ident;
else if(len == 9 && *user->ident == '~')
ident = user->ident+1;
else
return false;
for(int i = 0; i < 4; i++)
if(!HexToInt(ip[i], ident + i*2))
return false;
snprintf(newip, 16, "%d.%d.%d.%d", ip[0], ip[1], ip[2], ip[3]);
user->Extend("cgiirc_realhost", new std::string(user->host));
user->Extend("cgiirc_realip", new std::string(user->GetIPString()));
user->RemoveCloneCounts();
#ifdef IPV6
if (user->GetProtocolFamily() == AF_INET6)
inet_pton(AF_INET6, newip, &((sockaddr_in6*)user->ip)->sin6_addr);
else
#endif
inet_aton(newip, &((sockaddr_in*)user->ip)->sin_addr);
ServerInstance->AddLocalClone(user);
ServerInstance->AddGlobalClone(user);
user->CheckClass();
try
{
strlcpy(user->host, newip, 16);
strlcpy(user->dhost, newip, 16);
strlcpy(user->ident, "~cgiirc", 8);
bool cached;
CGIResolver* r = new CGIResolver(this, ServerInstance, NotifyOpers, newip, false, user, user->GetFd(), "IDENT", cached);
ServerInstance->AddResolver(r, cached);
}
catch (...)
{
strlcpy(user->host, newip, 16);
strlcpy(user->dhost, newip, 16);
strlcpy(user->ident, "~cgiirc", 8);
user->InvalidateCache();
if(NotifyOpers)
ServerInstance->WriteOpers("*** Connecting user %s detected as using CGI:IRC (%s), but i could not resolve their hostname!", user->nick, user->host);
}
/*strlcpy(user->host, newip, 16);
strlcpy(user->dhost, newip, 16);
strlcpy(user->ident, "~cgiirc", 8);*/
return true;
}
bool IsValidHost(const std::string &host)
{
if(!host.size())
return false;
for(unsigned int i = 0; i < host.size(); i++)
{
if( ((host[i] >= '0') && (host[i] <= '9')) ||
((host[i] >= 'A') && (host[i] <= 'Z')) ||
((host[i] >= 'a') && (host[i] <= 'z')) ||
((host[i] == '-') && (i > 0) && (i+1 < host.size()) && (host[i-1] != '.') && (host[i+1] != '.')) ||
((host[i] == '.') && (i > 0) && (i+1 < host.size())) )
continue;
else
return false;
}
return true;
}
bool IsValidIP(const std::string &ip)
{
if(ip.size() < 7 || ip.size() > 15)
return false;
short sincedot = 0;
short dots = 0;
for(unsigned int i = 0; i < ip.size(); i++)
{
if((dots <= 3) && (sincedot <= 3))
{
if((ip[i] >= '0') && (ip[i] <= '9'))
{
sincedot++;
}
else if(ip[i] == '.')
{
sincedot = 0;
dots++;
}
}
else
{
return false;
}
}
if(dots != 3)
return false;
return true;
}
bool HexToInt(int &out, const char* in)
{
char ip[3];
ip[0] = in[0];
ip[1] = in[1];
ip[2] = 0;
out = strtol(ip, NULL, 16);
if(out > 255 || out < 0)
return false;
return true;
}
virtual ~ModuleCgiIRC()
{
}
virtual Version GetVersion()
{
return Version(1,1,0,0,VF_VENDOR,API_VERSION);
}
};
MODULE_INIT(ModuleCgiIRC)
\ No newline at end of file +/* +------------------------------------+ + * | 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 "users.h" +#include "modules.h" +#include "dns.h" +#ifndef WINDOWS +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#endif + +/* $ModDesc: Change user's hosts connecting from known CGI:IRC hosts */ + +enum CGItype { PASS, IDENT, PASSFIRST, IDENTFIRST, WEBIRC }; + + +/** Holds a CGI site's details + */ +class CGIhost : public classbase +{ +public: + std::string hostmask; + CGItype type; + std::string password; + + CGIhost(const std::string &mask = "", CGItype t = IDENTFIRST, const std::string &password ="") + : hostmask(mask), type(t), password(password) + { + } +}; +typedef std::vector<CGIhost> CGIHostlist; + +class cmd_webirc : public command_t +{ + InspIRCd* Me; + CGIHostlist Hosts; + bool notify; + public: + cmd_webirc(InspIRCd* Me, CGIHostlist &Hosts, bool notify) : command_t(Me, "WEBIRC", 0, 4, true), Hosts(Hosts), notify(notify) + { + this->source = "m_cgiirc.so"; + this->syntax = "password client hostname ip"; + } + CmdResult Handle(const char** parameters, int pcnt, userrec *user) + { + if(user->registered == REG_ALL) + return CMD_FAILURE; + + for(CGIHostlist::iterator iter = Hosts.begin(); iter != Hosts.end(); iter++) + { + if(ServerInstance->MatchText(user->host, iter->hostmask) || ServerInstance->MatchText(user->GetIPString(), iter->hostmask)) + { + if(iter->type == WEBIRC && parameters[0] == iter->password) + { + user->Extend("cgiirc_realhost", new std::string(user->host)); + user->Extend("cgiirc_realip", new std::string(user->GetIPString())); + if (notify) + ServerInstance->WriteOpers("*** Connecting user %s detected as using CGI:IRC (%s), changing real host to %s from %s", user->nick, user->host, parameters[2], user->host); + user->Extend("cgiirc_webirc_hostname", new std::string(parameters[2])); + user->Extend("cgiirc_webirc_ip", new std::string(parameters[3])); + return CMD_LOCALONLY; + } + } + } + return CMD_FAILURE; + } +}; + + +/** Resolver for CGI:IRC hostnames encoded in ident/GECOS + */ +class CGIResolver : public Resolver +{ + std::string typ; + int theirfd; + userrec* them; + bool notify; + public: + CGIResolver(Module* me, InspIRCd* ServerInstance, bool NotifyOpers, const std::string &source, bool forward, userrec* u, int userfd, const std::string &type, bool &cached) + : Resolver(ServerInstance, source, forward ? DNS_QUERY_A : DNS_QUERY_PTR4, cached, me), typ(type), theirfd(userfd), them(u), notify(NotifyOpers) { } + + virtual void OnLookupComplete(const std::string &result, unsigned int ttl, bool cached) + { + /* Check the user still exists */ + if ((them) && (them == ServerInstance->SE->GetRef(theirfd))) + { + if (notify) + ServerInstance->WriteOpers("*** Connecting user %s detected as using CGI:IRC (%s), changing real host to %s from %s", them->nick, them->host, result.c_str(), typ.c_str()); + + strlcpy(them->host, result.c_str(), 63); + strlcpy(them->dhost, result.c_str(), 63); + strlcpy(them->ident, "~cgiirc", 8); + them->InvalidateCache(); + } + } + + virtual void OnError(ResolverError e, const std::string &errormessage) + { + if ((them) && (them == ServerInstance->SE->GetRef(theirfd))) + { + if (notify) + ServerInstance->WriteOpers("*** Connecting user %s detected as using CGI:IRC (%s), but their host can't be resolved from their %s!", them->nick, them->host,typ.c_str()); + } + } + + virtual ~CGIResolver() + { + } +}; + +class ModuleCgiIRC : public Module +{ + cmd_webirc* mycommand; + bool NotifyOpers; + CGIHostlist Hosts; +public: + ModuleCgiIRC(InspIRCd* Me) : Module(Me) + { + + OnRehash(NULL,""); + mycommand=new cmd_webirc(Me, Hosts, NotifyOpers); + ServerInstance->AddCommand(mycommand); + } + + void Implements(char* List) + { + List[I_OnRehash] = List[I_OnUserRegister] = List[I_OnCleanup] = List[I_OnSyncUserMetaData] = List[I_OnDecodeMetaData] = List[I_OnUserQuit] = List[I_OnUserConnect] = 1; + } + + virtual Priority Prioritize() + { + // We want to get here before m_cloaking and m_hostchange etc + return PRIORITY_FIRST; + } + + virtual void OnRehash(userrec* user, const std::string ¶meter) + { + ConfigReader Conf(ServerInstance); + + NotifyOpers = Conf.ReadFlag("cgiirc", "opernotice", 0); // If we send an oper notice when a CGI:IRC has their host changed. + + if(Conf.GetError() == CONF_VALUE_NOT_FOUND) + NotifyOpers = true; + + for(int i = 0; i < Conf.Enumerate("cgihost"); i++) + { + std::string hostmask = Conf.ReadValue("cgihost", "mask", i); // An allowed CGI:IRC host + std::string type = Conf.ReadValue("cgihost", "type", i); // What type of user-munging we do on this host. + std::string password = Conf.ReadValue("cgihost", "password", i); + + if(hostmask.length()) + { + if(type == "webirc" && !password.length()) { + ServerInstance->Log(DEFAULT, "m_cgiirc: Missing password in config: %s", hostmask.c_str()); + } else { + CGItype cgitype; + if(type == "pass") + cgitype = PASS; + else if(type == "ident") + cgitype = IDENT; + else if(type == "passfirst") + cgitype = PASSFIRST; + else if(type == "webirc") { + cgitype = WEBIRC; + } + Hosts.push_back(CGIhost(hostmask,cgitype, password.length() ? password : "" )); + } + } + else + { + ServerInstance->Log(DEFAULT, "m_cgiirc.so: Invalid <cgihost:mask> value in config: %s", hostmask.c_str()); + continue; + } + } + } + + virtual void OnCleanup(int target_type, void* item) + { + if(target_type == TYPE_USER) + { + userrec* user = (userrec*)item; + std::string* realhost; + std::string* realip; + + if(user->GetExt("cgiirc_realhost", realhost)) + { + delete realhost; + user->Shrink("cgiirc_realhost"); + } + + if(user->GetExt("cgiirc_realip", realip)) + { + delete realip; + user->Shrink("cgiirc_realip"); + } + } + } + + virtual void OnSyncUserMetaData(userrec* user, Module* proto, void* opaque, const std::string &extname, bool displayable) + { + if((extname == "cgiirc_realhost") || (extname == "cgiirc_realip")) + { + std::string* data; + + if(user->GetExt(extname, data)) + { + proto->ProtoSendMetaData(opaque, TYPE_USER, user, extname, *data); + } + } + } + + virtual void OnDecodeMetaData(int target_type, void* target, const std::string &extname, const std::string &extdata) + { + if(target_type == TYPE_USER) + { + userrec* dest = (userrec*)target; + std::string* bleh; + if(((extname == "cgiirc_realhost") || (extname == "cgiirc_realip")) && (!dest->GetExt(extname, bleh))) + { + dest->Extend(extname, new std::string(extdata)); + } + } + } + + virtual void OnUserQuit(userrec* user, const std::string &message, const std::string &oper_message) + { + OnCleanup(TYPE_USER, user); + } + + + virtual int OnUserRegister(userrec* user) + { + for(CGIHostlist::iterator iter = Hosts.begin(); iter != Hosts.end(); iter++) + { + if(ServerInstance->MatchText(user->host, iter->hostmask) || ServerInstance->MatchText(user->GetIPString(), iter->hostmask)) + { + // Deal with it... + if(iter->type == PASS) + { + CheckPass(user); // We do nothing if it fails so... + } + else if(iter->type == PASSFIRST && !CheckPass(user)) + { + // If the password lookup failed, try the ident + CheckIdent(user); // If this fails too, do nothing + } + else if(iter->type == IDENT) + { + CheckIdent(user); // Nothing on failure. + } + else if(iter->type == IDENTFIRST && !CheckIdent(user)) + { + // If the ident lookup fails, try the password. + CheckPass(user); + } + else if(iter->type == WEBIRC) + { + // We don't need to do anything here + } + return 0; + } + } + return 0; + } + + virtual void OnUserConnect(userrec* user) + { + std::string *webirc_hostname, *webirc_ip; + if(user->GetExt("cgiirc_webirc_hostname", webirc_hostname)) + { + strlcpy(user->host,webirc_hostname->c_str(),63); + strlcpy(user->dhost,webirc_hostname->c_str(),63); + delete webirc_hostname; + user->InvalidateCache(); + user->Shrink("cgiirc_webirc_hostname"); + } + if(user->GetExt("cgiirc_webirc_ip", webirc_ip)) + { + bool valid=false; + user->RemoveCloneCounts(); +#ifdef IPV6 + valid = (inet_pton(AF_INET6, webirc_ip->c_str(), &((sockaddr_in6*)user->ip)->sin6_addr) > 0); + + if(!valid) + valid = (inet_aton(webirc_ip->c_str(), &((sockaddr_in*)user->ip)->sin_addr)); +#else + if (inet_aton(webirc_ip->c_str(), &((sockaddr_in*)user->ip)->sin_addr)) + valid = true; +#endif + + delete webirc_ip; + user->InvalidateCache(); + user->Shrink("cgiirc_webirc_ip"); + ServerInstance->AddLocalClone(user); + ServerInstance->AddGlobalClone(user); + user->CheckClass(); + } + } + + bool CheckPass(userrec* user) + { + if(IsValidHost(user->password)) + { + user->Extend("cgiirc_realhost", new std::string(user->host)); + user->Extend("cgiirc_realip", new std::string(user->GetIPString())); + strlcpy(user->host, user->password, 64); + strlcpy(user->dhost, user->password, 64); + user->InvalidateCache(); + + bool valid = false; + user->RemoveCloneCounts(); +#ifdef IPV6 + if (user->GetProtocolFamily() == AF_INET6) + valid = (inet_pton(AF_INET6, user->password, &((sockaddr_in6*)user->ip)->sin6_addr) > 0); + else + valid = (inet_aton(user->password, &((sockaddr_in*)user->ip)->sin_addr)); +#else + if (inet_aton(user->password, &((sockaddr_in*)user->ip)->sin_addr)) + valid = true; +#endif + ServerInstance->AddLocalClone(user); + ServerInstance->AddGlobalClone(user); + user->CheckClass(); + + if (valid) + { + /* We were given a IP in the password, we don't do DNS so they get this is as their host as well. */ + if(NotifyOpers) + ServerInstance->WriteOpers("*** Connecting user %s detected as using CGI:IRC (%s), changing real host to %s from PASS", user->nick, user->host, user->password); + } + else + { + /* We got as resolved hostname in the password. */ + try + { + + bool cached; + CGIResolver* r = new CGIResolver(this, ServerInstance, NotifyOpers, user->password, false, user, user->GetFd(), "PASS", cached); + ServerInstance->AddResolver(r, cached); + } + catch (...) + { + if (NotifyOpers) + ServerInstance->WriteOpers("*** Connecting user %s detected as using CGI:IRC (%s), but i could not resolve their hostname!", user->nick, user->host); + } + } + + *user->password = 0; + + /*if(NotifyOpers) + ServerInstance->WriteOpers("*** Connecting user %s detected as using CGI:IRC (%s), changing real host to %s from PASS", user->nick, user->host, user->password);*/ + + return true; + } + + return false; + } + + bool CheckIdent(userrec* user) + { + int ip[4]; + char* ident; + char newip[16]; + int len = strlen(user->ident); + + if(len == 8) + ident = user->ident; + else if(len == 9 && *user->ident == '~') + ident = user->ident+1; + else + return false; + + for(int i = 0; i < 4; i++) + if(!HexToInt(ip[i], ident + i*2)) + return false; + + snprintf(newip, 16, "%d.%d.%d.%d", ip[0], ip[1], ip[2], ip[3]); + + user->Extend("cgiirc_realhost", new std::string(user->host)); + user->Extend("cgiirc_realip", new std::string(user->GetIPString())); + user->RemoveCloneCounts(); +#ifdef IPV6 + if (user->GetProtocolFamily() == AF_INET6) + inet_pton(AF_INET6, newip, &((sockaddr_in6*)user->ip)->sin6_addr); + else +#endif + inet_aton(newip, &((sockaddr_in*)user->ip)->sin_addr); + ServerInstance->AddLocalClone(user); + ServerInstance->AddGlobalClone(user); + user->CheckClass(); + try + { + strlcpy(user->host, newip, 16); + strlcpy(user->dhost, newip, 16); + strlcpy(user->ident, "~cgiirc", 8); + + bool cached; + CGIResolver* r = new CGIResolver(this, ServerInstance, NotifyOpers, newip, false, user, user->GetFd(), "IDENT", cached); + ServerInstance->AddResolver(r, cached); + } + catch (...) + { + strlcpy(user->host, newip, 16); + strlcpy(user->dhost, newip, 16); + strlcpy(user->ident, "~cgiirc", 8); + user->InvalidateCache(); + + if(NotifyOpers) + ServerInstance->WriteOpers("*** Connecting user %s detected as using CGI:IRC (%s), but i could not resolve their hostname!", user->nick, user->host); + } + /*strlcpy(user->host, newip, 16); + strlcpy(user->dhost, newip, 16); + strlcpy(user->ident, "~cgiirc", 8);*/ + + return true; + } + + bool IsValidHost(const std::string &host) + { + if(!host.size()) + return false; + + for(unsigned int i = 0; i < host.size(); i++) + { + if( ((host[i] >= '0') && (host[i] <= '9')) || + ((host[i] >= 'A') && (host[i] <= 'Z')) || + ((host[i] >= 'a') && (host[i] <= 'z')) || + ((host[i] == '-') && (i > 0) && (i+1 < host.size()) && (host[i-1] != '.') && (host[i+1] != '.')) || + ((host[i] == '.') && (i > 0) && (i+1 < host.size())) ) + + continue; + else + return false; + } + + return true; + } + + bool IsValidIP(const std::string &ip) + { + if(ip.size() < 7 || ip.size() > 15) + return false; + + short sincedot = 0; + short dots = 0; + + for(unsigned int i = 0; i < ip.size(); i++) + { + if((dots <= 3) && (sincedot <= 3)) + { + if((ip[i] >= '0') && (ip[i] <= '9')) + { + sincedot++; + } + else if(ip[i] == '.') + { + sincedot = 0; + dots++; + } + } + else + { + return false; + + } + } + + if(dots != 3) + return false; + + return true; + } + + bool HexToInt(int &out, const char* in) + { + char ip[3]; + ip[0] = in[0]; + ip[1] = in[1]; + ip[2] = 0; + out = strtol(ip, NULL, 16); + + if(out > 255 || out < 0) + return false; + + return true; + } + + virtual ~ModuleCgiIRC() + { + } + + virtual Version GetVersion() + { + return Version(1,1,0,0,VF_VENDOR,API_VERSION); + } + +}; + +MODULE_INIT(ModuleCgiIRC) diff --git a/src/modules/m_chancreate.cpp b/src/modules/m_chancreate.cpp index 8837db9c5..915e7c8cb 100644 --- a/src/modules/m_chancreate.cpp +++ b/src/modules/m_chancreate.cpp @@ -1 +1,55 @@ -/* +------------------------------------+
* | 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 "users.h"
#include "channels.h"
#include "modules.h"
/* $ModDesc: Creates a snomask with notices whenever a new channel is created */
class ModuleChanCreate : public Module
{
private:
public:
ModuleChanCreate(InspIRCd* Me)
: Module(Me)
{
ServerInstance->SNO->EnableSnomask('j', "CHANCREATE");
}
virtual ~ModuleChanCreate()
{
ServerInstance->SNO->DisableSnomask('j');
}
virtual Version GetVersion()
{
return Version(1,1,0,0,VF_VENDOR,API_VERSION);
}
void Implements(char* List)
{
List[I_OnUserJoin] = 1;
}
virtual void OnUserJoin(userrec* user, chanrec* channel, bool &silent)
{
if (channel->GetUserCounter() == 1)
{
ServerInstance->SNO->WriteToSnoMask('j', "Channel %s created by %s!%s@%s", channel->name, user->nick, user->ident, user->host);
}
}
};
MODULE_INIT(ModuleChanCreate)
\ No newline at end of file +/* +------------------------------------+ + * | 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 "users.h" +#include "channels.h" +#include "modules.h" + +/* $ModDesc: Creates a snomask with notices whenever a new channel is created */ + +class ModuleChanCreate : public Module +{ + private: + public: + ModuleChanCreate(InspIRCd* Me) + : Module(Me) + { + ServerInstance->SNO->EnableSnomask('j', "CHANCREATE"); + } + + virtual ~ModuleChanCreate() + { + ServerInstance->SNO->DisableSnomask('j'); + } + + virtual Version GetVersion() + { + return Version(1,1,0,0,VF_VENDOR,API_VERSION); + } + + void Implements(char* List) + { + List[I_OnUserJoin] = 1; + } + + virtual void OnUserJoin(userrec* user, chanrec* channel, bool &silent) + { + if (channel->GetUserCounter() == 1) + { + ServerInstance->SNO->WriteToSnoMask('j', "Channel %s created by %s!%s@%s", channel->name, user->nick, user->ident, user->host); + } + } +}; + +MODULE_INIT(ModuleChanCreate) diff --git a/src/modules/m_chanfilter.cpp b/src/modules/m_chanfilter.cpp index 44aac9dae..375fbce9c 100644 --- a/src/modules/m_chanfilter.cpp +++ b/src/modules/m_chanfilter.cpp @@ -1 +1,155 @@ -/* +------------------------------------+
* | 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.
*
* ---------------------------------------------------
*/
#define _CRT_SECURE_NO_DEPRECATE
#define _SCL_SECURE_NO_DEPRECATE
#include "inspircd.h"
#include "users.h"
#include "channels.h"
#include "modules.h"
#include "hashcomp.h"
#include "u_listmode.h"
/* $ModDesc: Provides channel-specific censor lists (like mode +G but varies from channel to channel) */
/* $ModDep: ../../include/u_listmode.h */
/** Handles channel mode +g
*/
class ChanFilter : public ListModeBase
{
public:
ChanFilter(InspIRCd* Instance) : ListModeBase(Instance, 'g', "End of channel spamfilter list", "941", "940", false, "chanfilter") { }
virtual bool ValidateParam(userrec* user, chanrec* chan, std::string &word)
{
if ((word.length() > 35) || (word.empty()))
{
user->WriteServ("935 %s %s %s :word is too %s for censor list",user->nick, chan->name,word.c_str(), (word.empty() ? "short" : "long"));
return false;
}
return true;
}
virtual bool TellListTooLong(userrec* user, chanrec* chan, std::string &word)
{
user->WriteServ("939 %s %s %s :Channel spamfilter list is full",user->nick, chan->name, word.c_str());
return true;
}
virtual void TellAlreadyOnList(userrec* user, chanrec* chan, std::string &word)
{
user->WriteServ("937 %s %s :The word %s is already on the spamfilter list",user->nick, chan->name,word.c_str());
}
virtual void TellNotSet(userrec* user, chanrec* chan, std::string &word)
{
user->WriteServ("938 %s %s :No such spamfilter word is set",user->nick, chan->name);
}
};
class ModuleChanFilter : public Module
{
ChanFilter* cf;
public:
ModuleChanFilter(InspIRCd* Me)
: Module(Me)
{
cf = new ChanFilter(ServerInstance);
if (!ServerInstance->AddMode(cf, 'g'))
throw ModuleException("Could not add new modes!");
}
void Implements(char* List)
{
cf->DoImplements(List);
List[I_OnCleanup] = List[I_OnChannelDelete] = List[I_OnRehash] = List[I_OnUserPreMessage] = List[I_OnUserPreNotice] = List[I_OnSyncChannel] = 1;
}
virtual void OnChannelDelete(chanrec* chan)
{
cf->DoChannelDelete(chan);
}
virtual void OnRehash(userrec* user, const std::string ¶meter)
{
cf->DoRehash();
}
virtual int ProcessMessages(userrec* user,chanrec* chan,std::string &text)
{
if (!IS_LOCAL(user) || CHANOPS_EXEMPT(ServerInstance, 'g') && chan->GetStatus(user) == STATUS_OP)
return 0;
// Create a copy of the string in irc::string
irc::string line = text.c_str();
modelist* list;
chan->GetExt(cf->GetInfoKey(), list);
if (list)
{
for (modelist::iterator i = list->begin(); i != list->end(); i++)
{
if (line.find(i->mask.c_str()) != std::string::npos)
{
user->WriteServ("936 %s %s %s :Your message contained a censored word, and was blocked",user->nick, chan->name, i->mask.c_str());
return 1;
}
}
}
return 0;
}
virtual int OnUserPreMessage(userrec* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list)
{
if (target_type == TYPE_CHANNEL)
{
return ProcessMessages(user,(chanrec*)dest,text);
}
else return 0;
}
virtual void OnCleanup(int target_type, void* item)
{
cf->DoCleanup(target_type, item);
}
virtual int OnUserPreNotice(userrec* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list)
{
return OnUserPreMessage(user,dest,target_type,text,status,exempt_list);
}
virtual void OnSyncChannel(chanrec* chan, Module* proto, void* opaque)
{
cf->DoSyncChannel(chan, proto, opaque);
}
virtual Version GetVersion()
{
return Version(1, 1, 0, 0, VF_COMMON | VF_VENDOR, API_VERSION);
}
virtual ~ModuleChanFilter()
{
ServerInstance->Modes->DelMode(cf);
DELETE(cf);
}
};
MODULE_INIT(ModuleChanFilter)
\ No newline at end of file +/* +------------------------------------+ + * | 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. + * + * --------------------------------------------------- + */ + +#define _CRT_SECURE_NO_DEPRECATE +#define _SCL_SECURE_NO_DEPRECATE + +#include "inspircd.h" +#include "users.h" +#include "channels.h" +#include "modules.h" +#include "hashcomp.h" +#include "u_listmode.h" + +/* $ModDesc: Provides channel-specific censor lists (like mode +G but varies from channel to channel) */ +/* $ModDep: ../../include/u_listmode.h */ + +/** Handles channel mode +g + */ +class ChanFilter : public ListModeBase +{ + public: + ChanFilter(InspIRCd* Instance) : ListModeBase(Instance, 'g', "End of channel spamfilter list", "941", "940", false, "chanfilter") { } + + virtual bool ValidateParam(userrec* user, chanrec* chan, std::string &word) + { + if ((word.length() > 35) || (word.empty())) + { + user->WriteServ("935 %s %s %s :word is too %s for censor list",user->nick, chan->name,word.c_str(), (word.empty() ? "short" : "long")); + return false; + } + + return true; + } + + virtual bool TellListTooLong(userrec* user, chanrec* chan, std::string &word) + { + user->WriteServ("939 %s %s %s :Channel spamfilter list is full",user->nick, chan->name, word.c_str()); + return true; + } + + virtual void TellAlreadyOnList(userrec* user, chanrec* chan, std::string &word) + { + user->WriteServ("937 %s %s :The word %s is already on the spamfilter list",user->nick, chan->name,word.c_str()); + } + + virtual void TellNotSet(userrec* user, chanrec* chan, std::string &word) + { + user->WriteServ("938 %s %s :No such spamfilter word is set",user->nick, chan->name); + } +}; + +class ModuleChanFilter : public Module +{ + + ChanFilter* cf; + + public: + + ModuleChanFilter(InspIRCd* Me) + : Module(Me) + { + cf = new ChanFilter(ServerInstance); + if (!ServerInstance->AddMode(cf, 'g')) + throw ModuleException("Could not add new modes!"); + } + + void Implements(char* List) + { + cf->DoImplements(List); + List[I_OnCleanup] = List[I_OnChannelDelete] = List[I_OnRehash] = List[I_OnUserPreMessage] = List[I_OnUserPreNotice] = List[I_OnSyncChannel] = 1; + } + + virtual void OnChannelDelete(chanrec* chan) + { + cf->DoChannelDelete(chan); + } + + virtual void OnRehash(userrec* user, const std::string ¶meter) + { + cf->DoRehash(); + } + + virtual int ProcessMessages(userrec* user,chanrec* chan,std::string &text) + { + if (!IS_LOCAL(user) || CHANOPS_EXEMPT(ServerInstance, 'g') && chan->GetStatus(user) == STATUS_OP) + return 0; + + // Create a copy of the string in irc::string + irc::string line = text.c_str(); + + modelist* list; + chan->GetExt(cf->GetInfoKey(), list); + + if (list) + { + for (modelist::iterator i = list->begin(); i != list->end(); i++) + { + if (line.find(i->mask.c_str()) != std::string::npos) + { + user->WriteServ("936 %s %s %s :Your message contained a censored word, and was blocked",user->nick, chan->name, i->mask.c_str()); + return 1; + } + } + } + + return 0; + } + + virtual int OnUserPreMessage(userrec* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list) + { + if (target_type == TYPE_CHANNEL) + { + return ProcessMessages(user,(chanrec*)dest,text); + } + else return 0; + } + + virtual void OnCleanup(int target_type, void* item) + { + cf->DoCleanup(target_type, item); + } + + virtual int OnUserPreNotice(userrec* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list) + { + return OnUserPreMessage(user,dest,target_type,text,status,exempt_list); + } + + virtual void OnSyncChannel(chanrec* chan, Module* proto, void* opaque) + { + cf->DoSyncChannel(chan, proto, opaque); + } + + virtual Version GetVersion() + { + return Version(1, 1, 0, 0, VF_COMMON | VF_VENDOR, API_VERSION); + } + + virtual ~ModuleChanFilter() + { + ServerInstance->Modes->DelMode(cf); + DELETE(cf); + } +}; + +MODULE_INIT(ModuleChanFilter) diff --git a/src/modules/m_chanprotect.cpp b/src/modules/m_chanprotect.cpp index 74640fe52..87bc1ca4c 100644 --- a/src/modules/m_chanprotect.cpp +++ b/src/modules/m_chanprotect.cpp @@ -1 +1,531 @@ -/* +------------------------------------+
* | 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 "users.h"
#include "channels.h"
#include "modules.h"
/* $ModDesc: Provides channel modes +a and +q */
/* $ModDep: ../../include/u_listmode.h */
#define PROTECT_VALUE 40000
#define FOUNDER_VALUE 50000
const char* fakevalue = "on";
/* When this is set to true, no restrictions apply to setting or
* removal of +qa. This is used while unloading so that the server
* can freely clear all of its users of the modes.
*/
bool unload_kludge = false;
/** Handles basic operation of +qa channel modes
*/
class FounderProtectBase
{
private:
InspIRCd* MyInstance;
std::string extend;
std::string type;
int list;
int end;
char* dummyptr;
protected:
bool& remove_own_privs;
bool& remove_other_privs;
public:
FounderProtectBase(InspIRCd* Instance, const std::string &ext, const std::string &mtype, int l, int e, bool &remove_own, bool &remove_others) :
MyInstance(Instance), extend(ext), type(mtype), list(l), end(e), remove_own_privs(remove_own), remove_other_privs(remove_others)
{
}
ModePair ModeSet(userrec* source, userrec* dest, chanrec* channel, const std::string ¶meter)
{
userrec* x = MyInstance->FindNick(parameter);
if (x)
{
if (!channel->HasUser(x))
{
return std::make_pair(false, parameter);
}
else
{
std::string item = extend+std::string(channel->name);
if (x->GetExt(item,dummyptr))
{
return std::make_pair(true, x->nick);
}
else
{
return std::make_pair(false, parameter);
}
}
}
return std::make_pair(false, parameter);
}
void RemoveMode(chanrec* channel, char mc)
{
unload_kludge = true;
CUList* cl = channel->GetUsers();
std::string item = extend + std::string(channel->name);
const char* mode_junk[MAXMODES+2];
userrec* n = new userrec(MyInstance);
n->SetFd(FD_MAGIC_NUMBER);
mode_junk[0] = channel->name;
irc::modestacker modestack(false);
std::deque<std::string> stackresult;
for (CUList::iterator i = cl->begin(); i != cl->end(); i++)
{
if (i->first->GetExt(item, dummyptr))
{
modestack.Push(mc, i->first->nick);
}
}
while (modestack.GetStackedLine(stackresult))
{
for (size_t j = 0; j < stackresult.size(); j++)
{
mode_junk[j+1] = stackresult[j].c_str();
}
MyInstance->SendMode(mode_junk, stackresult.size() + 1, n);
}
delete n;
unload_kludge = false;
}
void DisplayList(userrec* user, chanrec* channel)
{
CUList* cl = channel->GetUsers();
std::string item = extend+std::string(channel->name);
for (CUList::reverse_iterator i = cl->rbegin(); i != cl->rend(); ++i)
{
if (i->first->GetExt(item, dummyptr))
{
user->WriteServ("%d %s %s %s", list, user->nick, channel->name,i->first->nick);
}
}
user->WriteServ("%d %s %s :End of channel %s list", end, user->nick, channel->name, type.c_str());
}
userrec* FindAndVerify(std::string ¶meter, chanrec* channel)
{
userrec* theuser = MyInstance->FindNick(parameter);
if ((!theuser) || (!channel->HasUser(theuser)))
{
parameter.clear();
return NULL;
}
return theuser;
}
bool CanRemoveOthers(userrec* u1, userrec* u2, chanrec* c)
{
std::string item = extend+std::string(c->name);
return (u1->GetExt(item, dummyptr) && u2->GetExt(item, dummyptr));
}
ModeAction HandleChange(userrec* source, userrec* theuser, bool adding, chanrec* channel, std::string ¶meter)
{
std::string item = extend+std::string(channel->name);
if (adding)
{
if (!theuser->GetExt(item, dummyptr))
{
theuser->Extend(item, fakevalue);
parameter = theuser->nick;
return MODEACTION_ALLOW;
}
}
else
{
if (theuser->GetExt(item, dummyptr))
{
theuser->Shrink(item);
parameter = theuser->nick;
return MODEACTION_ALLOW;
}
}
return MODEACTION_DENY;
}
};
/** Abstraction of FounderProtectBase for channel mode +q
*/
class ChanFounder : public ModeHandler, public FounderProtectBase
{
char* dummyptr;
public:
ChanFounder(InspIRCd* Instance, bool using_prefixes, bool &depriv_self, bool &depriv_others)
: ModeHandler(Instance, 'q', 1, 1, true, MODETYPE_CHANNEL, false, using_prefixes ? '~' : 0),
FounderProtectBase(Instance, "cm_founder_", "founder", 386, 387, depriv_self, depriv_others) { }
unsigned int GetPrefixRank()
{
return FOUNDER_VALUE;
}
ModePair ModeSet(userrec* source, userrec* dest, chanrec* channel, const std::string ¶meter)
{
return FounderProtectBase::ModeSet(source, dest, channel, parameter);
}
void RemoveMode(chanrec* channel)
{
FounderProtectBase::RemoveMode(channel, this->GetModeChar());
}
void RemoveMode(userrec* user)
{
}
ModeAction OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string ¶meter, bool adding)
{
userrec* theuser = FounderProtectBase::FindAndVerify(parameter, channel);
if (!theuser)
{
return MODEACTION_DENY;
}
if ((!adding) && FounderProtectBase::CanRemoveOthers(source, theuser, channel))
{
return FounderProtectBase::HandleChange(source, theuser, adding, channel, parameter);
}
// source is a server, or ulined, we'll let them +-q the user.
if ((unload_kludge) || ((source == theuser) && (!adding) && (FounderProtectBase::remove_own_privs)) || (ServerInstance->ULine(source->nick)) || (ServerInstance->ULine(source->server)) || (!*source->server) || (!IS_LOCAL(source)))
{
return FounderProtectBase::HandleChange(source, theuser, adding, channel, parameter);
}
else
{
// whoops, someones being naughty!
source->WriteServ("468 %s %s :Only servers may set channel mode +q",source->nick, channel->name);
parameter.clear();
return MODEACTION_DENY;
}
}
void DisplayList(userrec* user, chanrec* channel)
{
FounderProtectBase::DisplayList(user,channel);
}
};
/** Abstraction of FounderProtectBase for channel mode +a
*/
class ChanProtect : public ModeHandler, public FounderProtectBase
{
char* dummyptr;
public:
ChanProtect(InspIRCd* Instance, bool using_prefixes, bool &depriv_self, bool &depriv_others)
: ModeHandler(Instance, 'a', 1, 1, true, MODETYPE_CHANNEL, false, using_prefixes ? '&' : 0),
FounderProtectBase(Instance,"cm_protect_","protected user", 388, 389, depriv_self, depriv_others) { }
unsigned int GetPrefixRank()
{
return PROTECT_VALUE;
}
ModePair ModeSet(userrec* source, userrec* dest, chanrec* channel, const std::string ¶meter)
{
return FounderProtectBase::ModeSet(source, dest, channel, parameter);
}
void RemoveMode(chanrec* channel)
{
FounderProtectBase::RemoveMode(channel, this->GetModeChar());
}
void RemoveMode(userrec* user)
{
}
ModeAction OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string ¶meter, bool adding)
{
userrec* theuser = FounderProtectBase::FindAndVerify(parameter, channel);
if (!theuser)
return MODEACTION_DENY;
std::string founder = "cm_founder_"+std::string(channel->name);
if ((!adding) && FounderProtectBase::CanRemoveOthers(source, theuser, channel))
{
return FounderProtectBase::HandleChange(source, theuser, adding, channel, parameter);
}
// source has +q, is a server, or ulined, we'll let them +-a the user.
if ((unload_kludge) || ((source == theuser) && (!adding) && (FounderProtectBase::remove_own_privs)) || (ServerInstance->ULine(source->nick)) || (ServerInstance->ULine(source->server)) || (!*source->server) || (source->GetExt(founder,dummyptr)) || (!IS_LOCAL(source)))
{
return FounderProtectBase::HandleChange(source, theuser, adding, channel, parameter);
}
else
{
// bzzzt, wrong answer!
source->WriteServ("482 %s %s :You are not a channel founder",source->nick, channel->name);
return MODEACTION_DENY;
}
}
virtual void DisplayList(userrec* user, chanrec* channel)
{
FounderProtectBase::DisplayList(user, channel);
}
};
class ModuleChanProtect : public Module
{
bool FirstInGetsFounder;
bool QAPrefixes;
bool DeprivSelf;
bool DeprivOthers;
bool booting;
ChanProtect* cp;
ChanFounder* cf;
char* dummyptr;
public:
ModuleChanProtect(InspIRCd* Me)
: Module(Me), FirstInGetsFounder(false), QAPrefixes(false), DeprivSelf(false), DeprivOthers(false), booting(true)
{
/* Load config stuff */
OnRehash(NULL,"");
booting = false;
/* Initialise module variables */
cp = new ChanProtect(ServerInstance,QAPrefixes,DeprivSelf,DeprivOthers);
cf = new ChanFounder(ServerInstance,QAPrefixes,DeprivSelf,DeprivOthers);
if (!ServerInstance->AddMode(cp, 'a') || !ServerInstance->AddMode(cf, 'q'))
throw ModuleException("Could not add new modes!");
}
void Implements(char* List)
{
List[I_OnUserKick] = List[I_OnUserPart] = List[I_OnRehash] = List[I_OnUserJoin] = List[I_OnAccessCheck] = List[I_OnSyncChannel] = 1;
}
virtual void OnUserKick(userrec* source, userrec* user, chanrec* chan, const std::string &reason, bool &silent)
{
// FIX: when someone gets kicked from a channel we must remove their Extensibles!
user->Shrink("cm_founder_"+std::string(chan->name));
user->Shrink("cm_protect_"+std::string(chan->name));
}
virtual void OnUserPart(userrec* user, chanrec* channel, const std::string &partreason, bool &silent)
{
// FIX: when someone parts a channel we must remove their Extensibles!
user->Shrink("cm_founder_"+std::string(channel->name));
user->Shrink("cm_protect_"+std::string(channel->name));
}
virtual void OnRehash(userrec* user, const std::string ¶meter)
{
/* Create a configreader class and read our flag,
* in old versions this was heap-allocated and the
* object was kept between rehashes...now we just
* stack-allocate it locally.
*/
ConfigReader Conf(ServerInstance);
bool old_qa = QAPrefixes;
FirstInGetsFounder = Conf.ReadFlag("options","noservices",0);
QAPrefixes = Conf.ReadFlag("options","qaprefixes",0);
DeprivSelf = Conf.ReadFlag("options","deprotectself",0);
DeprivOthers = Conf.ReadFlag("options","deprotectothers",0);
/* Did the user change the QA prefixes on the fly?
* If so, remove all instances of the mode, and reinit
* the module with prefixes enabled.
*/
if ((old_qa != QAPrefixes) && (!booting))
{
ServerInstance->Modes->DelMode(cp);
ServerInstance->Modes->DelMode(cf);
DELETE(cp);
DELETE(cf);
cp = new ChanProtect(ServerInstance,QAPrefixes,DeprivSelf,DeprivOthers);
cf = new ChanFounder(ServerInstance,QAPrefixes,DeprivSelf,DeprivOthers);
/* These wont fail, we already owned the mode characters before */
ServerInstance->AddMode(cp, 'a');
ServerInstance->AddMode(cf, 'q');
ServerInstance->WriteOpers("*** WARNING: +qa prefixes were enabled or disabled via a REHASH. Clients will probably need to reconnect to pick up this change.");
}
}
virtual void OnUserJoin(userrec* user, chanrec* channel, bool &silent)
{
// if the user is the first user into the channel, mark them as the founder, but only if
// the config option for it is set
if (FirstInGetsFounder)
{
if (channel->GetUserCounter() == 1)
{
// we're using Extensible::Extend to add data into user objects.
// this way is best as it adds data thats accessible to other modules
// (so long as you document your code properly) without breaking anything
// because its encapsulated neatly in a map.
// Change requested by katsklaw... when the first in is set to get founder,
// to make it clearer that +q has been given, send that one user the +q notice
// so that their client's syncronization and their sanity are left intact.
user->WriteServ("MODE %s +q %s",channel->name,user->nick);
user->Extend("cm_founder_"+std::string(channel->name),fakevalue);
}
}
}
virtual int OnAccessCheck(userrec* source,userrec* dest,chanrec* channel,int access_type)
{
// here we perform access checks, this is the important bit that actually stops kicking/deopping
// etc of protected users. There are many types of access check, we're going to handle
// a relatively small number of them relevent to our module using a switch statement.
// don't allow action if:
// (A) Theyre founder (no matter what)
// (B) Theyre protected, and you're not
// always allow the action if:
// (A) The source is ulined
// firstly, if a ulined nick, or a server, is setting the mode, then allow them to set the mode
// without any access checks, we're not worthy :p
if ((ServerInstance->ULine(source->nick)) || (ServerInstance->ULine(source->server)) || (!*source->server))
return ACR_ALLOW;
std::string founder = "cm_founder_"+std::string(channel->name);
std::string protect = "cm_protect_"+std::string(channel->name);
switch (access_type)
{
// a user has been deopped. Do we let them? hmmm...
case AC_DEOP:
if (dest->GetExt(founder,dummyptr))
{
source->WriteServ("484 "+std::string(source->nick)+" "+std::string(channel->name)+" :Can't deop "+std::string(dest->nick)+" as they're a channel founder");
return ACR_DENY;
}
if ((dest->GetExt(protect,dummyptr)) && (!source->GetExt(protect,dummyptr)))
{
source->WriteServ("484 "+std::string(source->nick)+" "+std::string(channel->name)+" :Can't deop "+std::string(dest->nick)+" as they're protected (+a)");
return ACR_DENY;
}
break;
// a user is being kicked. do we chop off the end of the army boot?
case AC_KICK:
if (dest->GetExt(founder,dummyptr))
{
source->WriteServ("484 "+std::string(source->nick)+" "+std::string(channel->name)+" :Can't kick "+std::string(dest->nick)+" as they're a channel founder");
return ACR_DENY;
}
if ((dest->GetExt(protect,dummyptr)) && (!source->GetExt(protect,dummyptr)))
{
source->WriteServ("484 "+std::string(source->nick)+" "+std::string(channel->name)+" :Can't kick "+std::string(dest->nick)+" as they're protected (+a)");
return ACR_DENY;
}
break;
// a user is being dehalfopped. Yes, we do disallow -h of a +ha user
case AC_DEHALFOP:
if (dest->GetExt(founder,dummyptr))
{
source->WriteServ("484 "+std::string(source->nick)+" "+std::string(channel->name)+" :Can't de-halfop "+std::string(dest->nick)+" as they're a channel founder");
return ACR_DENY;
}
if ((dest->GetExt(protect,dummyptr)) && (!source->GetExt(protect,dummyptr)))
{
source->WriteServ("484 "+std::string(source->nick)+" "+std::string(channel->name)+" :Can't de-halfop "+std::string(dest->nick)+" as they're protected (+a)");
return ACR_DENY;
}
break;
// same with devoice.
case AC_DEVOICE:
if (dest->GetExt(founder,dummyptr))
{
source->WriteServ("484 "+std::string(source->nick)+" "+std::string(channel->name)+" :Can't devoice "+std::string(dest->nick)+" as they're a channel founder");
return ACR_DENY;
}
if ((dest->GetExt(protect,dummyptr)) && (!source->GetExt(protect,dummyptr)))
{
source->WriteServ("484 "+std::string(source->nick)+" "+std::string(channel->name)+" :Can't devoice "+std::string(dest->nick)+" as they're protected (+a)");
return ACR_DENY;
}
break;
}
// we dont know what this access check is, or dont care. just carry on, nothing to see here.
return ACR_DEFAULT;
}
virtual ~ModuleChanProtect()
{
ServerInstance->Modes->DelMode(cp);
ServerInstance->Modes->DelMode(cf);
DELETE(cp);
DELETE(cf);
}
virtual Version GetVersion()
{
return Version(1, 1, 0, 0, VF_COMMON | VF_VENDOR, API_VERSION);
}
virtual void OnSyncChannel(chanrec* chan, Module* proto, void* opaque)
{
/* NOTE: If +qa prefix is on, this is propogated by the channel join,
* so we dont need to propogate it manually
*/
if (!QAPrefixes)
{
// this is called when the server is linking into a net and wants to sync channel data.
// we should send our mode changes for the channel here to ensure that other servers
// know whos +q/+a on the channel.
CUList* cl = chan->GetUsers();
string_list commands;
std::string founder = "cm_founder_"+std::string(chan->name);
std::string protect = "cm_protect_"+std::string(chan->name);
irc::modestacker modestack(true);
std::deque<std::string> stackresult;
for (CUList::iterator i = cl->begin(); i != cl->end(); i++)
{
if (i->first->GetExt(founder,dummyptr))
{
modestack.Push('q',i->first->nick);
}
if (i->first->GetExt(protect,dummyptr))
{
modestack.Push('a',i->first->nick);
}
}
while (modestack.GetStackedLine(stackresult))
{
irc::stringjoiner mode_join(" ", stackresult, 0, stackresult.size() - 1);
std::string line = mode_join.GetJoined();
proto->ProtoSendMode(opaque,TYPE_CHANNEL,chan, line);
}
}
}
};
MODULE_INIT(ModuleChanProtect)
\ No newline at end of file +/* +------------------------------------+ + * | 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 "users.h" +#include "channels.h" +#include "modules.h" + +/* $ModDesc: Provides channel modes +a and +q */ +/* $ModDep: ../../include/u_listmode.h */ + +#define PROTECT_VALUE 40000 +#define FOUNDER_VALUE 50000 + +const char* fakevalue = "on"; + +/* When this is set to true, no restrictions apply to setting or + * removal of +qa. This is used while unloading so that the server + * can freely clear all of its users of the modes. + */ +bool unload_kludge = false; + +/** Handles basic operation of +qa channel modes + */ +class FounderProtectBase +{ + private: + InspIRCd* MyInstance; + std::string extend; + std::string type; + int list; + int end; + char* dummyptr; + protected: + bool& remove_own_privs; + bool& remove_other_privs; + public: + FounderProtectBase(InspIRCd* Instance, const std::string &ext, const std::string &mtype, int l, int e, bool &remove_own, bool &remove_others) : + MyInstance(Instance), extend(ext), type(mtype), list(l), end(e), remove_own_privs(remove_own), remove_other_privs(remove_others) + { + } + + ModePair ModeSet(userrec* source, userrec* dest, chanrec* channel, const std::string ¶meter) + { + userrec* x = MyInstance->FindNick(parameter); + if (x) + { + if (!channel->HasUser(x)) + { + return std::make_pair(false, parameter); + } + else + { + std::string item = extend+std::string(channel->name); + if (x->GetExt(item,dummyptr)) + { + return std::make_pair(true, x->nick); + } + else + { + return std::make_pair(false, parameter); + } + } + } + return std::make_pair(false, parameter); + } + + void RemoveMode(chanrec* channel, char mc) + { + unload_kludge = true; + CUList* cl = channel->GetUsers(); + std::string item = extend + std::string(channel->name); + const char* mode_junk[MAXMODES+2]; + userrec* n = new userrec(MyInstance); + n->SetFd(FD_MAGIC_NUMBER); + mode_junk[0] = channel->name; + irc::modestacker modestack(false); + std::deque<std::string> stackresult; + for (CUList::iterator i = cl->begin(); i != cl->end(); i++) + { + if (i->first->GetExt(item, dummyptr)) + { + modestack.Push(mc, i->first->nick); + } + } + + while (modestack.GetStackedLine(stackresult)) + { + for (size_t j = 0; j < stackresult.size(); j++) + { + mode_junk[j+1] = stackresult[j].c_str(); + } + MyInstance->SendMode(mode_junk, stackresult.size() + 1, n); + } + + delete n; + unload_kludge = false; + } + + void DisplayList(userrec* user, chanrec* channel) + { + CUList* cl = channel->GetUsers(); + std::string item = extend+std::string(channel->name); + for (CUList::reverse_iterator i = cl->rbegin(); i != cl->rend(); ++i) + { + if (i->first->GetExt(item, dummyptr)) + { + user->WriteServ("%d %s %s %s", list, user->nick, channel->name,i->first->nick); + } + } + user->WriteServ("%d %s %s :End of channel %s list", end, user->nick, channel->name, type.c_str()); + } + + userrec* FindAndVerify(std::string ¶meter, chanrec* channel) + { + userrec* theuser = MyInstance->FindNick(parameter); + if ((!theuser) || (!channel->HasUser(theuser))) + { + parameter.clear(); + return NULL; + } + return theuser; + } + + bool CanRemoveOthers(userrec* u1, userrec* u2, chanrec* c) + { + std::string item = extend+std::string(c->name); + return (u1->GetExt(item, dummyptr) && u2->GetExt(item, dummyptr)); + } + + ModeAction HandleChange(userrec* source, userrec* theuser, bool adding, chanrec* channel, std::string ¶meter) + { + std::string item = extend+std::string(channel->name); + + if (adding) + { + if (!theuser->GetExt(item, dummyptr)) + { + theuser->Extend(item, fakevalue); + parameter = theuser->nick; + return MODEACTION_ALLOW; + } + } + else + { + if (theuser->GetExt(item, dummyptr)) + { + theuser->Shrink(item); + parameter = theuser->nick; + return MODEACTION_ALLOW; + } + } + return MODEACTION_DENY; + } +}; + +/** Abstraction of FounderProtectBase for channel mode +q + */ +class ChanFounder : public ModeHandler, public FounderProtectBase +{ + char* dummyptr; + public: + ChanFounder(InspIRCd* Instance, bool using_prefixes, bool &depriv_self, bool &depriv_others) + : ModeHandler(Instance, 'q', 1, 1, true, MODETYPE_CHANNEL, false, using_prefixes ? '~' : 0), + FounderProtectBase(Instance, "cm_founder_", "founder", 386, 387, depriv_self, depriv_others) { } + + unsigned int GetPrefixRank() + { + return FOUNDER_VALUE; + } + + ModePair ModeSet(userrec* source, userrec* dest, chanrec* channel, const std::string ¶meter) + { + return FounderProtectBase::ModeSet(source, dest, channel, parameter); + } + + void RemoveMode(chanrec* channel) + { + FounderProtectBase::RemoveMode(channel, this->GetModeChar()); + } + + void RemoveMode(userrec* user) + { + } + + ModeAction OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string ¶meter, bool adding) + { + userrec* theuser = FounderProtectBase::FindAndVerify(parameter, channel); + + if (!theuser) + { + return MODEACTION_DENY; + } + + if ((!adding) && FounderProtectBase::CanRemoveOthers(source, theuser, channel)) + { + return FounderProtectBase::HandleChange(source, theuser, adding, channel, parameter); + } + // source is a server, or ulined, we'll let them +-q the user. + if ((unload_kludge) || ((source == theuser) && (!adding) && (FounderProtectBase::remove_own_privs)) || (ServerInstance->ULine(source->nick)) || (ServerInstance->ULine(source->server)) || (!*source->server) || (!IS_LOCAL(source))) + { + return FounderProtectBase::HandleChange(source, theuser, adding, channel, parameter); + } + else + { + // whoops, someones being naughty! + source->WriteServ("468 %s %s :Only servers may set channel mode +q",source->nick, channel->name); + parameter.clear(); + return MODEACTION_DENY; + } + } + + void DisplayList(userrec* user, chanrec* channel) + { + FounderProtectBase::DisplayList(user,channel); + } +}; + +/** Abstraction of FounderProtectBase for channel mode +a + */ +class ChanProtect : public ModeHandler, public FounderProtectBase +{ + char* dummyptr; + public: + ChanProtect(InspIRCd* Instance, bool using_prefixes, bool &depriv_self, bool &depriv_others) + : ModeHandler(Instance, 'a', 1, 1, true, MODETYPE_CHANNEL, false, using_prefixes ? '&' : 0), + FounderProtectBase(Instance,"cm_protect_","protected user", 388, 389, depriv_self, depriv_others) { } + + unsigned int GetPrefixRank() + { + return PROTECT_VALUE; + } + + ModePair ModeSet(userrec* source, userrec* dest, chanrec* channel, const std::string ¶meter) + { + return FounderProtectBase::ModeSet(source, dest, channel, parameter); + } + + void RemoveMode(chanrec* channel) + { + FounderProtectBase::RemoveMode(channel, this->GetModeChar()); + } + + void RemoveMode(userrec* user) + { + } + + ModeAction OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string ¶meter, bool adding) + { + userrec* theuser = FounderProtectBase::FindAndVerify(parameter, channel); + + if (!theuser) + return MODEACTION_DENY; + + std::string founder = "cm_founder_"+std::string(channel->name); + + if ((!adding) && FounderProtectBase::CanRemoveOthers(source, theuser, channel)) + { + return FounderProtectBase::HandleChange(source, theuser, adding, channel, parameter); + } + // source has +q, is a server, or ulined, we'll let them +-a the user. + if ((unload_kludge) || ((source == theuser) && (!adding) && (FounderProtectBase::remove_own_privs)) || (ServerInstance->ULine(source->nick)) || (ServerInstance->ULine(source->server)) || (!*source->server) || (source->GetExt(founder,dummyptr)) || (!IS_LOCAL(source))) + { + return FounderProtectBase::HandleChange(source, theuser, adding, channel, parameter); + } + else + { + // bzzzt, wrong answer! + source->WriteServ("482 %s %s :You are not a channel founder",source->nick, channel->name); + return MODEACTION_DENY; + } + } + + virtual void DisplayList(userrec* user, chanrec* channel) + { + FounderProtectBase::DisplayList(user, channel); + } + +}; + +class ModuleChanProtect : public Module +{ + + bool FirstInGetsFounder; + bool QAPrefixes; + bool DeprivSelf; + bool DeprivOthers; + bool booting; + ChanProtect* cp; + ChanFounder* cf; + char* dummyptr; + + public: + + ModuleChanProtect(InspIRCd* Me) + : Module(Me), FirstInGetsFounder(false), QAPrefixes(false), DeprivSelf(false), DeprivOthers(false), booting(true) + { + /* Load config stuff */ + OnRehash(NULL,""); + booting = false; + + /* Initialise module variables */ + + cp = new ChanProtect(ServerInstance,QAPrefixes,DeprivSelf,DeprivOthers); + cf = new ChanFounder(ServerInstance,QAPrefixes,DeprivSelf,DeprivOthers); + + if (!ServerInstance->AddMode(cp, 'a') || !ServerInstance->AddMode(cf, 'q')) + throw ModuleException("Could not add new modes!"); + } + + void Implements(char* List) + { + List[I_OnUserKick] = List[I_OnUserPart] = List[I_OnRehash] = List[I_OnUserJoin] = List[I_OnAccessCheck] = List[I_OnSyncChannel] = 1; + } + + virtual void OnUserKick(userrec* source, userrec* user, chanrec* chan, const std::string &reason, bool &silent) + { + // FIX: when someone gets kicked from a channel we must remove their Extensibles! + user->Shrink("cm_founder_"+std::string(chan->name)); + user->Shrink("cm_protect_"+std::string(chan->name)); + } + + virtual void OnUserPart(userrec* user, chanrec* channel, const std::string &partreason, bool &silent) + { + // FIX: when someone parts a channel we must remove their Extensibles! + user->Shrink("cm_founder_"+std::string(channel->name)); + user->Shrink("cm_protect_"+std::string(channel->name)); + } + + virtual void OnRehash(userrec* user, const std::string ¶meter) + { + /* Create a configreader class and read our flag, + * in old versions this was heap-allocated and the + * object was kept between rehashes...now we just + * stack-allocate it locally. + */ + ConfigReader Conf(ServerInstance); + + bool old_qa = QAPrefixes; + + FirstInGetsFounder = Conf.ReadFlag("options","noservices",0); + QAPrefixes = Conf.ReadFlag("options","qaprefixes",0); + DeprivSelf = Conf.ReadFlag("options","deprotectself",0); + DeprivOthers = Conf.ReadFlag("options","deprotectothers",0); + + /* Did the user change the QA prefixes on the fly? + * If so, remove all instances of the mode, and reinit + * the module with prefixes enabled. + */ + if ((old_qa != QAPrefixes) && (!booting)) + { + ServerInstance->Modes->DelMode(cp); + ServerInstance->Modes->DelMode(cf); + DELETE(cp); + DELETE(cf); + cp = new ChanProtect(ServerInstance,QAPrefixes,DeprivSelf,DeprivOthers); + cf = new ChanFounder(ServerInstance,QAPrefixes,DeprivSelf,DeprivOthers); + /* These wont fail, we already owned the mode characters before */ + ServerInstance->AddMode(cp, 'a'); + ServerInstance->AddMode(cf, 'q'); + ServerInstance->WriteOpers("*** WARNING: +qa prefixes were enabled or disabled via a REHASH. Clients will probably need to reconnect to pick up this change."); + } + } + + virtual void OnUserJoin(userrec* user, chanrec* channel, bool &silent) + { + // if the user is the first user into the channel, mark them as the founder, but only if + // the config option for it is set + if (FirstInGetsFounder) + { + if (channel->GetUserCounter() == 1) + { + // we're using Extensible::Extend to add data into user objects. + // this way is best as it adds data thats accessible to other modules + // (so long as you document your code properly) without breaking anything + // because its encapsulated neatly in a map. + + // Change requested by katsklaw... when the first in is set to get founder, + // to make it clearer that +q has been given, send that one user the +q notice + // so that their client's syncronization and their sanity are left intact. + user->WriteServ("MODE %s +q %s",channel->name,user->nick); + user->Extend("cm_founder_"+std::string(channel->name),fakevalue); + } + } + } + + virtual int OnAccessCheck(userrec* source,userrec* dest,chanrec* channel,int access_type) + { + // here we perform access checks, this is the important bit that actually stops kicking/deopping + // etc of protected users. There are many types of access check, we're going to handle + // a relatively small number of them relevent to our module using a switch statement. + // don't allow action if: + // (A) Theyre founder (no matter what) + // (B) Theyre protected, and you're not + // always allow the action if: + // (A) The source is ulined + + + // firstly, if a ulined nick, or a server, is setting the mode, then allow them to set the mode + // without any access checks, we're not worthy :p + if ((ServerInstance->ULine(source->nick)) || (ServerInstance->ULine(source->server)) || (!*source->server)) + return ACR_ALLOW; + + std::string founder = "cm_founder_"+std::string(channel->name); + std::string protect = "cm_protect_"+std::string(channel->name); + + switch (access_type) + { + // a user has been deopped. Do we let them? hmmm... + case AC_DEOP: + if (dest->GetExt(founder,dummyptr)) + { + source->WriteServ("484 "+std::string(source->nick)+" "+std::string(channel->name)+" :Can't deop "+std::string(dest->nick)+" as they're a channel founder"); + return ACR_DENY; + } + if ((dest->GetExt(protect,dummyptr)) && (!source->GetExt(protect,dummyptr))) + { + source->WriteServ("484 "+std::string(source->nick)+" "+std::string(channel->name)+" :Can't deop "+std::string(dest->nick)+" as they're protected (+a)"); + return ACR_DENY; + } + break; + + // a user is being kicked. do we chop off the end of the army boot? + case AC_KICK: + if (dest->GetExt(founder,dummyptr)) + { + source->WriteServ("484 "+std::string(source->nick)+" "+std::string(channel->name)+" :Can't kick "+std::string(dest->nick)+" as they're a channel founder"); + return ACR_DENY; + } + if ((dest->GetExt(protect,dummyptr)) && (!source->GetExt(protect,dummyptr))) + { + source->WriteServ("484 "+std::string(source->nick)+" "+std::string(channel->name)+" :Can't kick "+std::string(dest->nick)+" as they're protected (+a)"); + return ACR_DENY; + } + break; + + // a user is being dehalfopped. Yes, we do disallow -h of a +ha user + case AC_DEHALFOP: + if (dest->GetExt(founder,dummyptr)) + { + source->WriteServ("484 "+std::string(source->nick)+" "+std::string(channel->name)+" :Can't de-halfop "+std::string(dest->nick)+" as they're a channel founder"); + return ACR_DENY; + } + if ((dest->GetExt(protect,dummyptr)) && (!source->GetExt(protect,dummyptr))) + { + source->WriteServ("484 "+std::string(source->nick)+" "+std::string(channel->name)+" :Can't de-halfop "+std::string(dest->nick)+" as they're protected (+a)"); + return ACR_DENY; + } + break; + + // same with devoice. + case AC_DEVOICE: + if (dest->GetExt(founder,dummyptr)) + { + source->WriteServ("484 "+std::string(source->nick)+" "+std::string(channel->name)+" :Can't devoice "+std::string(dest->nick)+" as they're a channel founder"); + return ACR_DENY; + } + if ((dest->GetExt(protect,dummyptr)) && (!source->GetExt(protect,dummyptr))) + { + source->WriteServ("484 "+std::string(source->nick)+" "+std::string(channel->name)+" :Can't devoice "+std::string(dest->nick)+" as they're protected (+a)"); + return ACR_DENY; + } + break; + } + + // we dont know what this access check is, or dont care. just carry on, nothing to see here. + return ACR_DEFAULT; + } + + virtual ~ModuleChanProtect() + { + ServerInstance->Modes->DelMode(cp); + ServerInstance->Modes->DelMode(cf); + DELETE(cp); + DELETE(cf); + } + + virtual Version GetVersion() + { + return Version(1, 1, 0, 0, VF_COMMON | VF_VENDOR, API_VERSION); + } + + virtual void OnSyncChannel(chanrec* chan, Module* proto, void* opaque) + { + /* NOTE: If +qa prefix is on, this is propogated by the channel join, + * so we dont need to propogate it manually + */ + if (!QAPrefixes) + { + // this is called when the server is linking into a net and wants to sync channel data. + // we should send our mode changes for the channel here to ensure that other servers + // know whos +q/+a on the channel. + CUList* cl = chan->GetUsers(); + string_list commands; + std::string founder = "cm_founder_"+std::string(chan->name); + std::string protect = "cm_protect_"+std::string(chan->name); + irc::modestacker modestack(true); + std::deque<std::string> stackresult; + for (CUList::iterator i = cl->begin(); i != cl->end(); i++) + { + if (i->first->GetExt(founder,dummyptr)) + { + modestack.Push('q',i->first->nick); + } + if (i->first->GetExt(protect,dummyptr)) + { + modestack.Push('a',i->first->nick); + } + } + while (modestack.GetStackedLine(stackresult)) + { + irc::stringjoiner mode_join(" ", stackresult, 0, stackresult.size() - 1); + std::string line = mode_join.GetJoined(); + proto->ProtoSendMode(opaque,TYPE_CHANNEL,chan, line); + } + } + } + +}; + +MODULE_INIT(ModuleChanProtect) diff --git a/src/modules/m_check.cpp b/src/modules/m_check.cpp index c99e985cc..643af8e15 100644 --- a/src/modules/m_check.cpp +++ b/src/modules/m_check.cpp @@ -1 +1,188 @@ -/* +------------------------------------+
* | 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 "users.h"
#include "channels.h"
#include "modules.h"
#include "wildcard.h"
/* $ModDesc: Provides the /check command to retrieve information on a user, channel, or IP address */
/** Handle /CHECK
*/
class cmd_check : public command_t
{
public:
cmd_check (InspIRCd* Instance) : command_t(Instance,"CHECK", 'o', 1)
{
this->source = "m_check.so";
syntax = "<nickname>|<ip>|<hostmask>|<channel>";
}
CmdResult Handle (const char** parameters, int pcnt, userrec *user)
{
userrec *targuser;
chanrec *targchan;
std::string checkstr;
std::string chliststr;
char timebuf[60];
struct tm *mytime;
checkstr = "304 " + std::string(user->nick) + " :CHECK";
targuser = ServerInstance->FindNick(parameters[0]);
targchan = ServerInstance->FindChan(parameters[0]);
/*
* Syntax of a /check reply:
* :server.name 304 target :CHECK START <target>
* :server.name 304 target :CHECK <field> <value>
* :server.name 304 target :CHECK END
*/
user->WriteServ(checkstr + " START " + parameters[0]);
if (targuser)
{
/* /check on a user */
user->WriteServ(checkstr + " nuh " + targuser->GetFullHost());
user->WriteServ(checkstr + " realnuh " + targuser->GetFullRealHost());
user->WriteServ(checkstr + " realname " + targuser->fullname);
user->WriteServ(checkstr + " modes +" + targuser->FormatModes());
user->WriteServ(checkstr + " snomasks +" + targuser->FormatNoticeMasks());
user->WriteServ(checkstr + " server " + targuser->server);
if (IS_AWAY(targuser))
{
/* user is away */
user->WriteServ(checkstr + " awaymsg " + targuser->awaymsg);
}
if (IS_OPER(targuser))
{
/* user is an oper of type ____ */
user->WriteServ(checkstr + " opertype " + irc::Spacify(targuser->oper));
}
if (IS_LOCAL(targuser))
{
/* port information is only held for a local user! */
user->WriteServ(checkstr + " onport " + ConvToStr(targuser->GetPort()));
}
chliststr = targuser->ChannelList(targuser);
std::stringstream dump(chliststr);
ServerInstance->DumpText(user,checkstr + " onchans ", dump);
}
else if (targchan)
{
/* /check on a channel */
time_t creation_time = targchan->created;
time_t topic_time = targchan->topicset;
mytime = gmtime(&creation_time);
strftime(timebuf, 59, "%Y/%m/%d - %H:%M:%S", mytime);
user->WriteServ(checkstr + " created " + timebuf);
if (targchan->topic[0] != 0)
{
/* there is a topic, assume topic related information exists */
user->WriteServ(checkstr + " topic " + targchan->topic);
user->WriteServ(checkstr + " topic_setby " + targchan->setby);
mytime = gmtime(&topic_time);
strftime(timebuf, 59, "%Y/%m/%d - %H:%M:%S", mytime);
user->WriteServ(checkstr + " topic_setat " + timebuf);
}
user->WriteServ(checkstr + " modes " + targchan->ChanModes(true));
user->WriteServ(checkstr + " membercount " + ConvToStr(targchan->GetUserCounter()));
/* now the ugly bit, spool current members of a channel. :| */
CUList *ulist= targchan->GetUsers();
/* note that unlike /names, we do NOT check +i vs in the channel */
for (CUList::iterator i = ulist->begin(); i != ulist->end(); i++)
{
char tmpbuf[MAXBUF];
/*
* Unlike Asuka, I define a clone as coming from the same host. --w00t
*/
snprintf(tmpbuf, MAXBUF, "%lu %s%s (%s@%s) %s ", i->first->GlobalCloneCount(), targchan->GetAllPrefixChars(i->first), i->first->nick, i->first->ident, i->first->dhost, i->first->fullname);
user->WriteServ(checkstr + " member " + tmpbuf);
}
}
else
{
/* /check on an IP address, or something that doesn't exist */
long x = 0;
/* hostname or other */
for (user_hash::const_iterator a = ServerInstance->clientlist->begin(); a != ServerInstance->clientlist->end(); a++)
{
if (match(a->second->host, parameters[0]) || match(a->second->dhost, parameters[0]))
{
/* host or vhost matches mask */
user->WriteServ(checkstr + " match " + ConvToStr(++x) + " " + a->second->GetFullRealHost());
}
/* IP address */
else if (match(a->second->GetIPString(), parameters[0], true))
{
/* same IP. */
user->WriteServ(checkstr + " match " + ConvToStr(++x) + " " + a->second->GetFullRealHost());
}
}
user->WriteServ(checkstr + " matches " + ConvToStr(x));
}
user->WriteServ(checkstr + " END " + std::string(parameters[0]));
return CMD_LOCALONLY;
}
};
class ModuleCheck : public Module
{
private:
cmd_check *mycommand;
public:
ModuleCheck(InspIRCd* Me) : Module(Me)
{
mycommand = new cmd_check(ServerInstance);
ServerInstance->AddCommand(mycommand);
}
virtual ~ModuleCheck()
{
}
virtual Version GetVersion()
{
return Version(1, 1, 0, 0, VF_VENDOR, API_VERSION);
}
void Implements(char* List)
{
/* we don't hook anything, nothing required */
}
};
MODULE_INIT(ModuleCheck)
\ No newline at end of file +/* +------------------------------------+ + * | 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 "users.h" +#include "channels.h" +#include "modules.h" +#include "wildcard.h" + +/* $ModDesc: Provides the /check command to retrieve information on a user, channel, or IP address */ + +/** Handle /CHECK + */ +class cmd_check : public command_t +{ + public: + cmd_check (InspIRCd* Instance) : command_t(Instance,"CHECK", 'o', 1) + { + this->source = "m_check.so"; + syntax = "<nickname>|<ip>|<hostmask>|<channel>"; + } + + CmdResult Handle (const char** parameters, int pcnt, userrec *user) + { + userrec *targuser; + chanrec *targchan; + std::string checkstr; + std::string chliststr; + + char timebuf[60]; + struct tm *mytime; + + + checkstr = "304 " + std::string(user->nick) + " :CHECK"; + + targuser = ServerInstance->FindNick(parameters[0]); + targchan = ServerInstance->FindChan(parameters[0]); + + /* + * Syntax of a /check reply: + * :server.name 304 target :CHECK START <target> + * :server.name 304 target :CHECK <field> <value> + * :server.name 304 target :CHECK END + */ + + user->WriteServ(checkstr + " START " + parameters[0]); + + if (targuser) + { + /* /check on a user */ + user->WriteServ(checkstr + " nuh " + targuser->GetFullHost()); + user->WriteServ(checkstr + " realnuh " + targuser->GetFullRealHost()); + user->WriteServ(checkstr + " realname " + targuser->fullname); + user->WriteServ(checkstr + " modes +" + targuser->FormatModes()); + user->WriteServ(checkstr + " snomasks +" + targuser->FormatNoticeMasks()); + user->WriteServ(checkstr + " server " + targuser->server); + + if (IS_AWAY(targuser)) + { + /* user is away */ + user->WriteServ(checkstr + " awaymsg " + targuser->awaymsg); + } + + if (IS_OPER(targuser)) + { + /* user is an oper of type ____ */ + user->WriteServ(checkstr + " opertype " + irc::Spacify(targuser->oper)); + } + + if (IS_LOCAL(targuser)) + { + /* port information is only held for a local user! */ + user->WriteServ(checkstr + " onport " + ConvToStr(targuser->GetPort())); + } + + chliststr = targuser->ChannelList(targuser); + std::stringstream dump(chliststr); + + ServerInstance->DumpText(user,checkstr + " onchans ", dump); + } + else if (targchan) + { + /* /check on a channel */ + time_t creation_time = targchan->created; + time_t topic_time = targchan->topicset; + + mytime = gmtime(&creation_time); + strftime(timebuf, 59, "%Y/%m/%d - %H:%M:%S", mytime); + user->WriteServ(checkstr + " created " + timebuf); + + if (targchan->topic[0] != 0) + { + /* there is a topic, assume topic related information exists */ + user->WriteServ(checkstr + " topic " + targchan->topic); + user->WriteServ(checkstr + " topic_setby " + targchan->setby); + mytime = gmtime(&topic_time); + strftime(timebuf, 59, "%Y/%m/%d - %H:%M:%S", mytime); + user->WriteServ(checkstr + " topic_setat " + timebuf); + } + + user->WriteServ(checkstr + " modes " + targchan->ChanModes(true)); + user->WriteServ(checkstr + " membercount " + ConvToStr(targchan->GetUserCounter())); + + /* now the ugly bit, spool current members of a channel. :| */ + + CUList *ulist= targchan->GetUsers(); + + /* note that unlike /names, we do NOT check +i vs in the channel */ + for (CUList::iterator i = ulist->begin(); i != ulist->end(); i++) + { + char tmpbuf[MAXBUF]; + /* + * Unlike Asuka, I define a clone as coming from the same host. --w00t + */ + snprintf(tmpbuf, MAXBUF, "%lu %s%s (%s@%s) %s ", i->first->GlobalCloneCount(), targchan->GetAllPrefixChars(i->first), i->first->nick, i->first->ident, i->first->dhost, i->first->fullname); + user->WriteServ(checkstr + " member " + tmpbuf); + } + } + else + { + /* /check on an IP address, or something that doesn't exist */ + long x = 0; + + /* hostname or other */ + for (user_hash::const_iterator a = ServerInstance->clientlist->begin(); a != ServerInstance->clientlist->end(); a++) + { + if (match(a->second->host, parameters[0]) || match(a->second->dhost, parameters[0])) + { + /* host or vhost matches mask */ + user->WriteServ(checkstr + " match " + ConvToStr(++x) + " " + a->second->GetFullRealHost()); + } + /* IP address */ + else if (match(a->second->GetIPString(), parameters[0], true)) + { + /* same IP. */ + user->WriteServ(checkstr + " match " + ConvToStr(++x) + " " + a->second->GetFullRealHost()); + } + } + + user->WriteServ(checkstr + " matches " + ConvToStr(x)); + } + + user->WriteServ(checkstr + " END " + std::string(parameters[0])); + + return CMD_LOCALONLY; + } +}; + + +class ModuleCheck : public Module +{ + private: + cmd_check *mycommand; + public: + ModuleCheck(InspIRCd* Me) : Module(Me) + { + + mycommand = new cmd_check(ServerInstance); + ServerInstance->AddCommand(mycommand); + } + + virtual ~ModuleCheck() + { + } + + virtual Version GetVersion() + { + return Version(1, 1, 0, 0, VF_VENDOR, API_VERSION); + } + + void Implements(char* List) + { + /* we don't hook anything, nothing required */ + } + +}; + +MODULE_INIT(ModuleCheck) diff --git a/src/modules/m_chghost.cpp b/src/modules/m_chghost.cpp index 9fb751b8e..0ec88d7e1 100644 --- a/src/modules/m_chghost.cpp +++ b/src/modules/m_chghost.cpp @@ -1 +1,120 @@ -/* +------------------------------------+
* | 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 "users.h"
#include "channels.h"
#include "modules.h"
/* $ModDesc: Provides support for the CHGHOST command */
/** Handle /CHGHOST
*/
class cmd_chghost : public command_t
{
private:
char* hostmap;
public:
cmd_chghost (InspIRCd* Instance, char* hmap) : command_t(Instance,"CHGHOST",'o',2), hostmap(hmap)
{
this->source = "m_chghost.so";
syntax = "<nick> <newhost>";
}
CmdResult Handle(const char** parameters, int pcnt, userrec *user)
{
const char * x = parameters[1];
for (; *x; x++)
{
if (!hostmap[(unsigned char)*x])
{
user->WriteServ("NOTICE "+std::string(user->nick)+" :*** CHGHOST: Invalid characters in hostname");
return CMD_FAILURE;
}
}
if (!*parameters[0])
{
user->WriteServ("NOTICE %s :*** CHGHOST: Host must be specified", user->nick);
return CMD_FAILURE;
}
if ((parameters[1] - x) > 63)
{
user->WriteServ("NOTICE %s :*** CHGHOST: Host too long", user->nick);
return CMD_FAILURE;
}
userrec* dest = ServerInstance->FindNick(parameters[0]);
if (!dest)
{
user->WriteServ("401 %s %s :No such nick/channel", user->nick, parameters[0]);
return CMD_FAILURE;
}
if ((dest->ChangeDisplayedHost(parameters[1])) && (!ServerInstance->ULine(user->server)))
{
// fix by brain - ulines set hosts silently
ServerInstance->WriteOpers(std::string(user->nick)+" used CHGHOST to make the displayed host of "+dest->nick+" become "+dest->dhost);
}
/* route it! */
return CMD_SUCCESS;
}
};
class ModuleChgHost : public Module
{
cmd_chghost* mycommand;
char hostmap[256];
public:
ModuleChgHost(InspIRCd* Me)
: Module(Me)
{
OnRehash(NULL,"");
mycommand = new cmd_chghost(ServerInstance, hostmap);
ServerInstance->AddCommand(mycommand);
}
void Implements(char* List)
{
List[I_OnRehash] = 1;
}
void OnRehash(userrec* user, const std::string ¶meter)
{
ConfigReader Conf(ServerInstance);
std::string hmap = Conf.ReadValue("hostname", "charmap", 0);
if (hmap.empty())
hmap = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz.-_/0123456789";
memset(&hostmap, 0, 255);
for (std::string::iterator n = hmap.begin(); n != hmap.end(); n++)
hostmap[(unsigned char)*n] = 1;
}
~ModuleChgHost()
{
}
Version GetVersion()
{
return Version(1, 1, 0, 0, VF_VENDOR, API_VERSION);
}
};
MODULE_INIT(ModuleChgHost)
\ No newline at end of file +/* +------------------------------------+ + * | 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 "users.h" +#include "channels.h" +#include "modules.h" + +/* $ModDesc: Provides support for the CHGHOST command */ + +/** Handle /CHGHOST + */ +class cmd_chghost : public command_t +{ + private: + char* hostmap; + public: + cmd_chghost (InspIRCd* Instance, char* hmap) : command_t(Instance,"CHGHOST",'o',2), hostmap(hmap) + { + this->source = "m_chghost.so"; + syntax = "<nick> <newhost>"; + } + + CmdResult Handle(const char** parameters, int pcnt, userrec *user) + { + const char * x = parameters[1]; + + for (; *x; x++) + { + if (!hostmap[(unsigned char)*x]) + { + user->WriteServ("NOTICE "+std::string(user->nick)+" :*** CHGHOST: Invalid characters in hostname"); + return CMD_FAILURE; + } + } + if (!*parameters[0]) + { + user->WriteServ("NOTICE %s :*** CHGHOST: Host must be specified", user->nick); + return CMD_FAILURE; + } + + if ((parameters[1] - x) > 63) + { + user->WriteServ("NOTICE %s :*** CHGHOST: Host too long", user->nick); + return CMD_FAILURE; + } + userrec* dest = ServerInstance->FindNick(parameters[0]); + + if (!dest) + { + user->WriteServ("401 %s %s :No such nick/channel", user->nick, parameters[0]); + return CMD_FAILURE; + } + + if ((dest->ChangeDisplayedHost(parameters[1])) && (!ServerInstance->ULine(user->server))) + { + // fix by brain - ulines set hosts silently + ServerInstance->WriteOpers(std::string(user->nick)+" used CHGHOST to make the displayed host of "+dest->nick+" become "+dest->dhost); + } + + /* route it! */ + return CMD_SUCCESS; + + } +}; + + +class ModuleChgHost : public Module +{ + cmd_chghost* mycommand; + char hostmap[256]; + public: + ModuleChgHost(InspIRCd* Me) + : Module(Me) + { + OnRehash(NULL,""); + mycommand = new cmd_chghost(ServerInstance, hostmap); + ServerInstance->AddCommand(mycommand); + } + + void Implements(char* List) + { + List[I_OnRehash] = 1; + } + + void OnRehash(userrec* user, const std::string ¶meter) + { + ConfigReader Conf(ServerInstance); + std::string hmap = Conf.ReadValue("hostname", "charmap", 0); + + if (hmap.empty()) + hmap = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz.-_/0123456789"; + + memset(&hostmap, 0, 255); + for (std::string::iterator n = hmap.begin(); n != hmap.end(); n++) + hostmap[(unsigned char)*n] = 1; + } + + ~ModuleChgHost() + { + } + + Version GetVersion() + { + return Version(1, 1, 0, 0, VF_VENDOR, API_VERSION); + } + +}; + +MODULE_INIT(ModuleChgHost) diff --git a/src/modules/m_chgident.cpp b/src/modules/m_chgident.cpp index fb909b7ff..168adcc93 100644 --- a/src/modules/m_chgident.cpp +++ b/src/modules/m_chgident.cpp @@ -1 +1,92 @@ -/* +------------------------------------+
* | 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 "users.h"
#include "modules.h"
/* $ModDesc: Provides support for the CHGIDENT command */
/** Handle /CHGIDENT
*/
class cmd_chgident : public command_t
{
public:
cmd_chgident (InspIRCd* Instance) : command_t(Instance,"CHGIDENT", 'o', 2)
{
this->source = "m_chgident.so";
syntax = "<nick> <newident>";
}
CmdResult Handle(const char** parameters, int pcnt, userrec *user)
{
userrec* dest = ServerInstance->FindNick(parameters[0]);
if (!dest)
{
user->WriteServ("401 %s %s :No such nick/channel", user->nick, parameters[0]);
return CMD_FAILURE;
}
if (!*parameters[1])
{
user->WriteServ("NOTICE %s :*** CHGIDENT: Ident must be specified", user->nick);
return CMD_FAILURE;
}
if (strlen(parameters[1]) > IDENTMAX)
{
user->WriteServ("NOTICE %s :*** CHGIDENT: Ident is too long", user->nick);
return CMD_FAILURE;
}
if (!ServerInstance->IsIdent(parameters[1]))
{
user->WriteServ("NOTICE %s :*** CHGIDENT: Invalid characters in ident", user->nick);
return CMD_FAILURE;
}
dest->ChangeIdent(parameters[1]);
ServerInstance->WriteOpers("%s used CHGIDENT to change %s's ident to '%s'", user->nick, dest->nick, dest->ident);
/* route it! */
return CMD_SUCCESS;
}
};
class ModuleChgIdent : public Module
{
cmd_chgident* mycommand;
public:
ModuleChgIdent(InspIRCd* Me) : Module(Me)
{
mycommand = new cmd_chgident(ServerInstance);
ServerInstance->AddCommand(mycommand);
}
virtual ~ModuleChgIdent()
{
}
virtual Version GetVersion()
{
return Version(1,1,0,0,VF_VENDOR,API_VERSION);
}
};
MODULE_INIT(ModuleChgIdent)
\ No newline at end of file +/* +------------------------------------+ + * | 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 "users.h" +#include "modules.h" + +/* $ModDesc: Provides support for the CHGIDENT command */ + +/** Handle /CHGIDENT + */ +class cmd_chgident : public command_t +{ + public: + cmd_chgident (InspIRCd* Instance) : command_t(Instance,"CHGIDENT", 'o', 2) + { + this->source = "m_chgident.so"; + syntax = "<nick> <newident>"; + } + + CmdResult Handle(const char** parameters, int pcnt, userrec *user) + { + userrec* dest = ServerInstance->FindNick(parameters[0]); + + if (!dest) + { + user->WriteServ("401 %s %s :No such nick/channel", user->nick, parameters[0]); + return CMD_FAILURE; + } + + if (!*parameters[1]) + { + user->WriteServ("NOTICE %s :*** CHGIDENT: Ident must be specified", user->nick); + return CMD_FAILURE; + } + + if (strlen(parameters[1]) > IDENTMAX) + { + user->WriteServ("NOTICE %s :*** CHGIDENT: Ident is too long", user->nick); + return CMD_FAILURE; + } + + if (!ServerInstance->IsIdent(parameters[1])) + { + user->WriteServ("NOTICE %s :*** CHGIDENT: Invalid characters in ident", user->nick); + return CMD_FAILURE; + } + + dest->ChangeIdent(parameters[1]); + ServerInstance->WriteOpers("%s used CHGIDENT to change %s's ident to '%s'", user->nick, dest->nick, dest->ident); + + /* route it! */ + return CMD_SUCCESS; + } +}; + + +class ModuleChgIdent : public Module +{ + cmd_chgident* mycommand; + + +public: + ModuleChgIdent(InspIRCd* Me) : Module(Me) + { + mycommand = new cmd_chgident(ServerInstance); + ServerInstance->AddCommand(mycommand); + } + + virtual ~ModuleChgIdent() + { + } + + virtual Version GetVersion() + { + return Version(1,1,0,0,VF_VENDOR,API_VERSION); + } + +}; + +MODULE_INIT(ModuleChgIdent) + diff --git a/src/modules/m_chgname.cpp b/src/modules/m_chgname.cpp index 0bf9004dd..a4a31714b 100644 --- a/src/modules/m_chgname.cpp +++ b/src/modules/m_chgname.cpp @@ -1 +1,89 @@ -/* +------------------------------------+
* | 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 "users.h"
#include "modules.h"
/* $ModDesc: Provides support for the CHGNAME command */
/** Handle /CHGNAME
*/
class cmd_chgname : public command_t
{
public:
cmd_chgname (InspIRCd* Instance) : command_t(Instance,"CHGNAME", 'o', 2)
{
this->source = "m_chgname.so";
syntax = "<nick> <newname>";
}
CmdResult Handle(const char** parameters, int pcnt, userrec *user)
{
userrec* dest = ServerInstance->FindNick(parameters[0]);
if (!dest)
{
user->WriteServ("401 %s %s :No such nick/channel", user->nick, parameters[0]);
return CMD_FAILURE;
}
if (!*parameters[1])
{
user->WriteServ("NOTICE %s :*** GECOS must be specified", user->nick);
return CMD_FAILURE;
}
if (strlen(parameters[1]) > MAXGECOS)
{
user->WriteServ("NOTICE %s :*** GECOS too long", user->nick);
return CMD_FAILURE;
}
if (IS_LOCAL(dest))
{
dest->ChangeName(parameters[1]);
ServerInstance->WriteOpers("%s used CHGNAME to change %s's real name to '%s'", user->nick, dest->nick, dest->fullname);
return CMD_LOCALONLY; /* name change routed by FNAME in spanningtree now */
}
/* route it! */
return CMD_SUCCESS;
}
};
class ModuleChgName : public Module
{
cmd_chgname* mycommand;
public:
ModuleChgName(InspIRCd* Me) : Module(Me)
{
mycommand = new cmd_chgname(ServerInstance);
ServerInstance->AddCommand(mycommand);
}
virtual ~ModuleChgName()
{
}
virtual Version GetVersion()
{
return Version(1,1,0,0,VF_VENDOR,API_VERSION);
}
};
MODULE_INIT(ModuleChgName)
\ No newline at end of file +/* +------------------------------------+ + * | 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 "users.h" +#include "modules.h" + +/* $ModDesc: Provides support for the CHGNAME command */ + +/** Handle /CHGNAME + */ +class cmd_chgname : public command_t +{ + public: + cmd_chgname (InspIRCd* Instance) : command_t(Instance,"CHGNAME", 'o', 2) + { + this->source = "m_chgname.so"; + syntax = "<nick> <newname>"; + } + + CmdResult Handle(const char** parameters, int pcnt, userrec *user) + { + userrec* dest = ServerInstance->FindNick(parameters[0]); + + if (!dest) + { + user->WriteServ("401 %s %s :No such nick/channel", user->nick, parameters[0]); + return CMD_FAILURE; + } + + if (!*parameters[1]) + { + user->WriteServ("NOTICE %s :*** GECOS must be specified", user->nick); + return CMD_FAILURE; + } + + if (strlen(parameters[1]) > MAXGECOS) + { + user->WriteServ("NOTICE %s :*** GECOS too long", user->nick); + return CMD_FAILURE; + } + + if (IS_LOCAL(dest)) + { + dest->ChangeName(parameters[1]); + ServerInstance->WriteOpers("%s used CHGNAME to change %s's real name to '%s'", user->nick, dest->nick, dest->fullname); + return CMD_LOCALONLY; /* name change routed by FNAME in spanningtree now */ + } + + /* route it! */ + return CMD_SUCCESS; + } +}; + + +class ModuleChgName : public Module +{ + cmd_chgname* mycommand; + + +public: + ModuleChgName(InspIRCd* Me) : Module(Me) + { + mycommand = new cmd_chgname(ServerInstance); + ServerInstance->AddCommand(mycommand); + } + + virtual ~ModuleChgName() + { + } + + virtual Version GetVersion() + { + return Version(1,1,0,0,VF_VENDOR,API_VERSION); + } + +}; + +MODULE_INIT(ModuleChgName) diff --git a/src/modules/m_cloaking.cpp b/src/modules/m_cloaking.cpp index d7992301c..dfa5ee4e8 100644 --- a/src/modules/m_cloaking.cpp +++ b/src/modules/m_cloaking.cpp @@ -1 +1,315 @@ -/* +------------------------------------+
* | 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 "users.h"
#include "channels.h"
#include "modules.h"
#include "m_hash.h"
/* $ModDesc: Provides masking of user hostnames */
/* $ModDep: m_hash.h */
/* Used to vary the output a little more depending on the cloak keys */
static const char* xtab[] = {"F92E45D871BCA630", "A1B9D80C72E653F4", "1ABC078934DEF562", "ABCDEF5678901234"};
/** Handles user mode +x
*/
class CloakUser : public ModeHandler
{
std::string prefix;
unsigned int key1;
unsigned int key2;
unsigned int key3;
unsigned int key4;
Module* Sender;
Module* HashProvider;
/** This function takes a domain name string and returns just the last two domain parts,
* or the last domain part if only two are available. Failing that it just returns what it was given.
*
* For example, if it is passed "svn.inspircd.org" it will return ".inspircd.org".
* If it is passed "brainbox.winbot.co.uk" it will return ".co.uk",
* and if it is passed "localhost.localdomain" it will return ".localdomain".
*
* This is used to ensure a significant part of the host is always cloaked (see Bug #216)
*/
std::string LastTwoDomainParts(const std::string &host)
{
int dots = 0;
std::string::size_type splitdot = host.length();
for (std::string::size_type x = host.length() - 1; x; --x)
{
if (host[x] == '.')
{
splitdot = x;
dots++;
}
if (dots >= 3)
break;
}
if (splitdot == host.length())
return host;
else
return host.substr(splitdot);
}
public:
CloakUser(InspIRCd* Instance, Module* Source, Module* Hash) : ModeHandler(Instance, 'x', 0, 0, false, MODETYPE_USER, false), Sender(Source), HashProvider(Hash)
{
}
ModeAction OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string ¶meter, bool adding)
{
if (source != dest)
return MODEACTION_DENY;
/* For remote clients, we dont take any action, we just allow it.
* The local server where they are will set their cloak instead.
*/
if (!IS_LOCAL(dest))
return MODEACTION_ALLOW;
if (adding)
{
if(!dest->IsModeSet('x'))
{
/* The mode is being turned on - so attempt to
* allocate the user a cloaked host using a non-reversible
* algorithm (its simple, but its non-reversible so the
* simplicity doesnt really matter). This algorithm
* will not work if the user has only one level of domain
* naming in their hostname (e.g. if they are on a lan or
* are connecting via localhost) -- this doesnt matter much.
*/
char* n1 = strchr(dest->host,'.');
char* n2 = strchr(dest->host,':');
if (n1 || n2)
{
/* InspIRCd users have two hostnames; A displayed
* hostname which can be modified by modules (e.g.
* to create vhosts, implement chghost, etc) and a
* 'real' hostname which you shouldnt write to.
*/
unsigned int iv[] = { key1, key2, key3, key4 };
std::string a = LastTwoDomainParts(dest->host);
std::string b;
/** Reset the Hash module, and send it our IV and hex table */
HashResetRequest(Sender, HashProvider).Send();
HashKeyRequest(Sender, HashProvider, iv).Send();
HashHexRequest(Sender, HashProvider, xtab[(*dest->host) % 4]);
/* Generate a cloak using specialized Hash */
std::string hostcloak = prefix + "-" + std::string(HashSumRequest(Sender, HashProvider, dest->host).Send()).substr(0,8) + a;
/* Fix by brain - if the cloaked host is > the max length of a host (64 bytes
* according to the DNS RFC) then tough titty, they get cloaked as an IP.
* Their ISP shouldnt go to town on subdomains, or they shouldnt have a kiddie
* vhost.
*/
#ifdef IPV6
in6_addr testaddr;
in_addr testaddr2;
if ((dest->GetProtocolFamily() == AF_INET6) && (inet_pton(AF_INET6,dest->host,&testaddr) < 1) && (hostcloak.length() <= 64))
/* Invalid ipv6 address, and ipv6 user (resolved host) */
b = hostcloak;
else if ((dest->GetProtocolFamily() == AF_INET) && (inet_aton(dest->host,&testaddr2) < 1) && (hostcloak.length() <= 64))
/* Invalid ipv4 address, and ipv4 user (resolved host) */
b = hostcloak;
else
/* Valid ipv6 or ipv4 address (not resolved) ipv4 or ipv6 user */
b = ((!strchr(dest->host,':')) ? Cloak4(dest->host) : Cloak6(dest->host));
#else
in_addr testaddr;
if ((inet_aton(dest->host,&testaddr) < 1) && (hostcloak.length() <= 64))
/* Invalid ipv4 address, and ipv4 user (resolved host) */
b = hostcloak;
else
/* Valid ipv4 address (not resolved) ipv4 user */
b = Cloak4(dest->host);
#endif
dest->ChangeDisplayedHost(b.c_str());
}
dest->SetMode('x',true);
return MODEACTION_ALLOW;
}
}
else
{
if (dest->IsModeSet('x'))
{
/* User is removing the mode, so just restore their real host
* and make it match the displayed one.
*/
dest->ChangeDisplayedHost(dest->host);
dest->SetMode('x',false);
return MODEACTION_ALLOW;
}
}
return MODEACTION_DENY;
}
std::string Cloak4(const char* ip)
{
unsigned int iv[] = { key1, key2, key3, key4 };
irc::sepstream seps(ip, '.');
std::string ra[4];;
std::string octet[4];
int i[4];
for (int j = 0; j < 4; j++)
{
octet[j] = seps.GetToken();
i[j] = atoi(octet[j].c_str());
}
octet[3] = octet[0] + "." + octet[1] + "." + octet[2] + "." + octet[3];
octet[2] = octet[0] + "." + octet[1] + "." + octet[2];
octet[1] = octet[0] + "." + octet[1];
/* Reset the Hash module and send it our IV */
HashResetRequest(Sender, HashProvider).Send();
HashKeyRequest(Sender, HashProvider, iv).Send();
/* Send the Hash module a different hex table for each octet group's Hash sum */
for (int k = 0; k < 4; k++)
{
HashHexRequest(Sender, HashProvider, xtab[(iv[k]+i[k]) % 4]).Send();
ra[k] = std::string(HashSumRequest(Sender, HashProvider, octet[k]).Send()).substr(0,6);
}
/* Stick them all together */
return std::string().append(ra[0]).append(".").append(ra[1]).append(".").append(ra[2]).append(".").append(ra[3]);
}
std::string Cloak6(const char* ip)
{
/* Theyre using 4in6 (YUCK). Translate as ipv4 cloak */
if (!strncmp(ip, "0::ffff:", 8))
return Cloak4(ip + 8);
/* If we get here, yes it really is an ipv6 ip */
unsigned int iv[] = { key1, key2, key3, key4 };
std::vector<std::string> hashies;
std::string item;
int rounds = 0;
/* Reset the Hash module and send it our IV */
HashResetRequest(Sender, HashProvider).Send();
HashKeyRequest(Sender, HashProvider, iv).Send();
for (const char* input = ip; *input; input++)
{
item += *input;
if (item.length() > 7)
{
/* Send the Hash module a different hex table for each octet group's Hash sum */
HashHexRequest(Sender, HashProvider, xtab[(key1+rounds) % 4]).Send();
hashies.push_back(std::string(HashSumRequest(Sender, HashProvider, item).Send()).substr(0,8));
item.clear();
}
rounds++;
}
if (!item.empty())
{
/* Send the Hash module a different hex table for each octet group's Hash sum */
HashHexRequest(Sender, HashProvider, xtab[(key1+rounds) % 4]).Send();
hashies.push_back(std::string(HashSumRequest(Sender, HashProvider, item).Send()).substr(0,8));
item.clear();
}
/* Stick them all together */
return irc::stringjoiner(":", hashies, 0, hashies.size() - 1).GetJoined();
}
void DoRehash()
{
ConfigReader Conf(ServerInstance);
key1 = key2 = key3 = key4 = 0;
key1 = Conf.ReadInteger("cloak","key1",0,true);
key2 = Conf.ReadInteger("cloak","key2",0,true);
key3 = Conf.ReadInteger("cloak","key3",0,true);
key4 = Conf.ReadInteger("cloak","key4",0,true);
prefix = Conf.ReadValue("cloak","prefix",0);
if (prefix.empty())
prefix = ServerInstance->Config->Network;
if (!key1 && !key2 && !key3 && !key4)
throw ModuleException("You have not defined cloak keys for m_cloaking!!! THIS IS INSECURE AND SHOULD BE CHECKED!");
}
};
class ModuleCloaking : public Module
{
private:
CloakUser* cu;
Module* HashModule;
public:
ModuleCloaking(InspIRCd* Me)
: Module(Me)
{
ServerInstance->UseInterface("HashRequest");
/* Attempt to locate the md5 service provider, bail if we can't find it */
HashModule = ServerInstance->FindModule("m_md5.so");
if (!HashModule)
throw ModuleException("Can't find m_md5.so. Please load m_md5.so before m_cloaking.so.");
/* Create new mode handler object */
cu = new CloakUser(ServerInstance, this, HashModule);
/* Register it with the core */
if (!ServerInstance->AddMode(cu, 'x'))
throw ModuleException("Could not add new modes!");
OnRehash(NULL,"");
}
virtual ~ModuleCloaking()
{
ServerInstance->Modes->DelMode(cu);
DELETE(cu);
ServerInstance->DoneWithInterface("HashRequest");
}
virtual Version GetVersion()
{
// returns the version number of the module to be
// listed in /MODULES
return Version(1,1,0,2,VF_COMMON|VF_VENDOR,API_VERSION);
}
virtual void OnRehash(userrec* user, const std::string ¶meter)
{
cu->DoRehash();
}
void Implements(char* List)
{
List[I_OnRehash] = 1;
}
};
MODULE_INIT(ModuleCloaking)
\ No newline at end of file +/* +------------------------------------+ + * | 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 "users.h" +#include "channels.h" +#include "modules.h" +#include "m_hash.h" + +/* $ModDesc: Provides masking of user hostnames */ +/* $ModDep: m_hash.h */ + +/* Used to vary the output a little more depending on the cloak keys */ +static const char* xtab[] = {"F92E45D871BCA630", "A1B9D80C72E653F4", "1ABC078934DEF562", "ABCDEF5678901234"}; + +/** Handles user mode +x + */ +class CloakUser : public ModeHandler +{ + + std::string prefix; + unsigned int key1; + unsigned int key2; + unsigned int key3; + unsigned int key4; + Module* Sender; + Module* HashProvider; + + /** This function takes a domain name string and returns just the last two domain parts, + * or the last domain part if only two are available. Failing that it just returns what it was given. + * + * For example, if it is passed "svn.inspircd.org" it will return ".inspircd.org". + * If it is passed "brainbox.winbot.co.uk" it will return ".co.uk", + * and if it is passed "localhost.localdomain" it will return ".localdomain". + * + * This is used to ensure a significant part of the host is always cloaked (see Bug #216) + */ + std::string LastTwoDomainParts(const std::string &host) + { + int dots = 0; + std::string::size_type splitdot = host.length(); + + for (std::string::size_type x = host.length() - 1; x; --x) + { + if (host[x] == '.') + { + splitdot = x; + dots++; + } + if (dots >= 3) + break; + } + + if (splitdot == host.length()) + return host; + else + return host.substr(splitdot); + } + + public: + CloakUser(InspIRCd* Instance, Module* Source, Module* Hash) : ModeHandler(Instance, 'x', 0, 0, false, MODETYPE_USER, false), Sender(Source), HashProvider(Hash) + { + } + + ModeAction OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string ¶meter, bool adding) + { + if (source != dest) + return MODEACTION_DENY; + + /* For remote clients, we dont take any action, we just allow it. + * The local server where they are will set their cloak instead. + */ + if (!IS_LOCAL(dest)) + return MODEACTION_ALLOW; + + if (adding) + { + if(!dest->IsModeSet('x')) + { + /* The mode is being turned on - so attempt to + * allocate the user a cloaked host using a non-reversible + * algorithm (its simple, but its non-reversible so the + * simplicity doesnt really matter). This algorithm + * will not work if the user has only one level of domain + * naming in their hostname (e.g. if they are on a lan or + * are connecting via localhost) -- this doesnt matter much. + */ + + char* n1 = strchr(dest->host,'.'); + char* n2 = strchr(dest->host,':'); + + if (n1 || n2) + { + /* InspIRCd users have two hostnames; A displayed + * hostname which can be modified by modules (e.g. + * to create vhosts, implement chghost, etc) and a + * 'real' hostname which you shouldnt write to. + */ + + unsigned int iv[] = { key1, key2, key3, key4 }; + std::string a = LastTwoDomainParts(dest->host); + std::string b; + + /** Reset the Hash module, and send it our IV and hex table */ + HashResetRequest(Sender, HashProvider).Send(); + HashKeyRequest(Sender, HashProvider, iv).Send(); + HashHexRequest(Sender, HashProvider, xtab[(*dest->host) % 4]); + + /* Generate a cloak using specialized Hash */ + std::string hostcloak = prefix + "-" + std::string(HashSumRequest(Sender, HashProvider, dest->host).Send()).substr(0,8) + a; + + /* Fix by brain - if the cloaked host is > the max length of a host (64 bytes + * according to the DNS RFC) then tough titty, they get cloaked as an IP. + * Their ISP shouldnt go to town on subdomains, or they shouldnt have a kiddie + * vhost. + */ +#ifdef IPV6 + in6_addr testaddr; + in_addr testaddr2; + if ((dest->GetProtocolFamily() == AF_INET6) && (inet_pton(AF_INET6,dest->host,&testaddr) < 1) && (hostcloak.length() <= 64)) + /* Invalid ipv6 address, and ipv6 user (resolved host) */ + b = hostcloak; + else if ((dest->GetProtocolFamily() == AF_INET) && (inet_aton(dest->host,&testaddr2) < 1) && (hostcloak.length() <= 64)) + /* Invalid ipv4 address, and ipv4 user (resolved host) */ + b = hostcloak; + else + /* Valid ipv6 or ipv4 address (not resolved) ipv4 or ipv6 user */ + b = ((!strchr(dest->host,':')) ? Cloak4(dest->host) : Cloak6(dest->host)); +#else + in_addr testaddr; + if ((inet_aton(dest->host,&testaddr) < 1) && (hostcloak.length() <= 64)) + /* Invalid ipv4 address, and ipv4 user (resolved host) */ + b = hostcloak; + else + /* Valid ipv4 address (not resolved) ipv4 user */ + b = Cloak4(dest->host); +#endif + + dest->ChangeDisplayedHost(b.c_str()); + } + + dest->SetMode('x',true); + return MODEACTION_ALLOW; + } + } + else + { + if (dest->IsModeSet('x')) + { + /* User is removing the mode, so just restore their real host + * and make it match the displayed one. + */ + dest->ChangeDisplayedHost(dest->host); + dest->SetMode('x',false); + return MODEACTION_ALLOW; + } + } + + return MODEACTION_DENY; + } + + std::string Cloak4(const char* ip) + { + unsigned int iv[] = { key1, key2, key3, key4 }; + irc::sepstream seps(ip, '.'); + std::string ra[4];; + std::string octet[4]; + int i[4]; + + for (int j = 0; j < 4; j++) + { + octet[j] = seps.GetToken(); + i[j] = atoi(octet[j].c_str()); + } + + octet[3] = octet[0] + "." + octet[1] + "." + octet[2] + "." + octet[3]; + octet[2] = octet[0] + "." + octet[1] + "." + octet[2]; + octet[1] = octet[0] + "." + octet[1]; + + /* Reset the Hash module and send it our IV */ + HashResetRequest(Sender, HashProvider).Send(); + HashKeyRequest(Sender, HashProvider, iv).Send(); + + /* Send the Hash module a different hex table for each octet group's Hash sum */ + for (int k = 0; k < 4; k++) + { + HashHexRequest(Sender, HashProvider, xtab[(iv[k]+i[k]) % 4]).Send(); + ra[k] = std::string(HashSumRequest(Sender, HashProvider, octet[k]).Send()).substr(0,6); + } + /* Stick them all together */ + return std::string().append(ra[0]).append(".").append(ra[1]).append(".").append(ra[2]).append(".").append(ra[3]); + } + + std::string Cloak6(const char* ip) + { + /* Theyre using 4in6 (YUCK). Translate as ipv4 cloak */ + if (!strncmp(ip, "0::ffff:", 8)) + return Cloak4(ip + 8); + + /* If we get here, yes it really is an ipv6 ip */ + unsigned int iv[] = { key1, key2, key3, key4 }; + std::vector<std::string> hashies; + std::string item; + int rounds = 0; + + /* Reset the Hash module and send it our IV */ + HashResetRequest(Sender, HashProvider).Send(); + HashKeyRequest(Sender, HashProvider, iv).Send(); + + for (const char* input = ip; *input; input++) + { + item += *input; + if (item.length() > 7) + { + /* Send the Hash module a different hex table for each octet group's Hash sum */ + HashHexRequest(Sender, HashProvider, xtab[(key1+rounds) % 4]).Send(); + hashies.push_back(std::string(HashSumRequest(Sender, HashProvider, item).Send()).substr(0,8)); + item.clear(); + } + rounds++; + } + if (!item.empty()) + { + /* Send the Hash module a different hex table for each octet group's Hash sum */ + HashHexRequest(Sender, HashProvider, xtab[(key1+rounds) % 4]).Send(); + hashies.push_back(std::string(HashSumRequest(Sender, HashProvider, item).Send()).substr(0,8)); + item.clear(); + } + /* Stick them all together */ + return irc::stringjoiner(":", hashies, 0, hashies.size() - 1).GetJoined(); + } + + void DoRehash() + { + ConfigReader Conf(ServerInstance); + key1 = key2 = key3 = key4 = 0; + key1 = Conf.ReadInteger("cloak","key1",0,true); + key2 = Conf.ReadInteger("cloak","key2",0,true); + key3 = Conf.ReadInteger("cloak","key3",0,true); + key4 = Conf.ReadInteger("cloak","key4",0,true); + prefix = Conf.ReadValue("cloak","prefix",0); + + if (prefix.empty()) + prefix = ServerInstance->Config->Network; + + if (!key1 && !key2 && !key3 && !key4) + throw ModuleException("You have not defined cloak keys for m_cloaking!!! THIS IS INSECURE AND SHOULD BE CHECKED!"); + } +}; + + +class ModuleCloaking : public Module +{ + private: + + CloakUser* cu; + Module* HashModule; + + public: + ModuleCloaking(InspIRCd* Me) + : Module(Me) + { + ServerInstance->UseInterface("HashRequest"); + + /* Attempt to locate the md5 service provider, bail if we can't find it */ + HashModule = ServerInstance->FindModule("m_md5.so"); + if (!HashModule) + throw ModuleException("Can't find m_md5.so. Please load m_md5.so before m_cloaking.so."); + + /* Create new mode handler object */ + cu = new CloakUser(ServerInstance, this, HashModule); + + /* Register it with the core */ + if (!ServerInstance->AddMode(cu, 'x')) + throw ModuleException("Could not add new modes!"); + + OnRehash(NULL,""); + } + + virtual ~ModuleCloaking() + { + ServerInstance->Modes->DelMode(cu); + DELETE(cu); + ServerInstance->DoneWithInterface("HashRequest"); + } + + virtual Version GetVersion() + { + // returns the version number of the module to be + // listed in /MODULES + return Version(1,1,0,2,VF_COMMON|VF_VENDOR,API_VERSION); + } + + virtual void OnRehash(userrec* user, const std::string ¶meter) + { + cu->DoRehash(); + } + + void Implements(char* List) + { + List[I_OnRehash] = 1; + } +}; + +MODULE_INIT(ModuleCloaking) diff --git a/src/modules/m_clones.cpp b/src/modules/m_clones.cpp index 429b9c2b9..72a7ca7e8 100644 --- a/src/modules/m_clones.cpp +++ b/src/modules/m_clones.cpp @@ -1 +1,100 @@ -/* +------------------------------------+
* | 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 "users.h"
#include "channels.h"
#include "modules.h"
#include "wildcard.h"
/* $ModDesc: Provides the /clones command to retrieve information on a user, channel, or IP address */
/** Handle /CHECK
*/
class cmd_clones : public command_t
{
public:
cmd_clones (InspIRCd* Instance) : command_t(Instance,"CLONES", 'o', 1)
{
this->source = "m_clones.so";
syntax = "<limit>";
}
std::string FindMatchingIP(const irc::string &ipaddr)
{
std::string n = assign(ipaddr);
for (user_hash::const_iterator a = ServerInstance->clientlist->begin(); a != ServerInstance->clientlist->end(); a++)
if (a->second->GetIPString() == n)
return a->second->GetFullRealHost();
return "<?>";
}
CmdResult Handle (const char** parameters, int pcnt, userrec *user)
{
std::string clonesstr = "304 " + std::string(user->nick) + " :CLONES";
unsigned long limit = atoi(parameters[0]);
/*
* Syntax of a /clones reply:
* :server.name 304 target :CLONES START
* :server.name 304 target :CLONES <count> <ip> <fullhost>
* :server.name 304 target :CHECK END
*/
user->WriteServ(clonesstr + " START");
/* hostname or other */
for (clonemap::iterator x = ServerInstance->global_clones.begin(); x != ServerInstance->global_clones.end(); x++)
{
if (x->second >= limit)
user->WriteServ(clonesstr + " "+ ConvToStr(x->second) + " " + assign(x->first) + " " + FindMatchingIP(x->first));
}
user->WriteServ(clonesstr + " END");
return CMD_LOCALONLY;
}
};
class ModuleClones : public Module
{
private:
cmd_clones *mycommand;
public:
ModuleClones(InspIRCd* Me) : Module(Me)
{
mycommand = new cmd_clones(ServerInstance);
ServerInstance->AddCommand(mycommand);
}
virtual ~ModuleClones()
{
}
virtual Version GetVersion()
{
return Version(1, 1, 0, 0, VF_VENDOR, API_VERSION);
}
void Implements(char* List)
{
/* we don't hook anything, nothing required */
}
};
MODULE_INIT(ModuleClones)
\ No newline at end of file +/* +------------------------------------+ + * | 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 "users.h" +#include "channels.h" +#include "modules.h" +#include "wildcard.h" + +/* $ModDesc: Provides the /clones command to retrieve information on a user, channel, or IP address */ + +/** Handle /CHECK + */ +class cmd_clones : public command_t +{ + public: + cmd_clones (InspIRCd* Instance) : command_t(Instance,"CLONES", 'o', 1) + { + this->source = "m_clones.so"; + syntax = "<limit>"; + } + + std::string FindMatchingIP(const irc::string &ipaddr) + { + std::string n = assign(ipaddr); + for (user_hash::const_iterator a = ServerInstance->clientlist->begin(); a != ServerInstance->clientlist->end(); a++) + if (a->second->GetIPString() == n) + return a->second->GetFullRealHost(); + return "<?>"; + } + + CmdResult Handle (const char** parameters, int pcnt, userrec *user) + { + + std::string clonesstr = "304 " + std::string(user->nick) + " :CLONES"; + + unsigned long limit = atoi(parameters[0]); + + /* + * Syntax of a /clones reply: + * :server.name 304 target :CLONES START + * :server.name 304 target :CLONES <count> <ip> <fullhost> + * :server.name 304 target :CHECK END + */ + + user->WriteServ(clonesstr + " START"); + + /* hostname or other */ + for (clonemap::iterator x = ServerInstance->global_clones.begin(); x != ServerInstance->global_clones.end(); x++) + { + if (x->second >= limit) + user->WriteServ(clonesstr + " "+ ConvToStr(x->second) + " " + assign(x->first) + " " + FindMatchingIP(x->first)); + } + + user->WriteServ(clonesstr + " END"); + + return CMD_LOCALONLY; + } +}; + + +class ModuleClones : public Module +{ + private: + cmd_clones *mycommand; + public: + ModuleClones(InspIRCd* Me) : Module(Me) + { + + mycommand = new cmd_clones(ServerInstance); + ServerInstance->AddCommand(mycommand); + } + + virtual ~ModuleClones() + { + } + + virtual Version GetVersion() + { + return Version(1, 1, 0, 0, VF_VENDOR, API_VERSION); + } + + void Implements(char* List) + { + /* we don't hook anything, nothing required */ + } + +}; + +MODULE_INIT(ModuleClones) diff --git a/src/modules/m_conn_join.cpp b/src/modules/m_conn_join.cpp index a8e81fcd8..2d639f310 100644 --- a/src/modules/m_conn_join.cpp +++ b/src/modules/m_conn_join.cpp @@ -1 +1,96 @@ -/* +------------------------------------+
* | 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 "users.h"
#include "channels.h"
#include "modules.h"
/* $ModDesc: Forces users to join the specified channel(s) on connect */
class ModuleConnJoin : public Module
{
private:
std::string JoinChan;
std::vector<std::string> Joinchans;
int tokenize(const string &str, std::vector<std::string> &tokens)
{
// skip delimiters at beginning.
string::size_type lastPos = str.find_first_not_of(",", 0);
// find first "non-delimiter".
string::size_type pos = str.find_first_of(",", lastPos);
while (string::npos != pos || string::npos != lastPos)
{
// found a token, add it to the vector.
tokens.push_back(str.substr(lastPos, pos - lastPos));
// skip delimiters. Note the "not_of"
lastPos = str.find_first_not_of(",", pos);
// find next "non-delimiter"
pos = str.find_first_of(",", lastPos);
}
return tokens.size();
}
public:
ModuleConnJoin(InspIRCd* Me)
: Module(Me)
{
OnRehash(NULL, "");
}
Priority Prioritize()
{
return PRIORITY_LAST;
}
void Implements(char* List)
{
List[I_OnPostConnect] = List[I_OnRehash] = 1;
}
virtual void OnRehash(userrec* user, const std::string ¶meter)
{
ConfigReader* conf = new ConfigReader(ServerInstance);
JoinChan = conf->ReadValue("autojoin", "channel", 0);
Joinchans.clear();
if (!JoinChan.empty())
tokenize(JoinChan,Joinchans);
DELETE(conf);
}
virtual ~ModuleConnJoin()
{
}
virtual Version GetVersion()
{
return Version(1,1,0,1,VF_VENDOR,API_VERSION);
}
virtual void OnPostConnect(userrec* user)
{
if (!IS_LOCAL(user))
return;
for(std::vector<std::string>::iterator it = Joinchans.begin(); it != Joinchans.end(); it++)
if (ServerInstance->IsChannel(it->c_str()))
chanrec::JoinUser(ServerInstance, user, it->c_str(), false, "", ServerInstance->Time(true));
}
};
MODULE_INIT(ModuleConnJoin)
\ No newline at end of file +/* +------------------------------------+ + * | 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 "users.h" +#include "channels.h" +#include "modules.h" + +/* $ModDesc: Forces users to join the specified channel(s) on connect */ + +class ModuleConnJoin : public Module +{ + private: + std::string JoinChan; + std::vector<std::string> Joinchans; + + + int tokenize(const string &str, std::vector<std::string> &tokens) + { + // skip delimiters at beginning. + string::size_type lastPos = str.find_first_not_of(",", 0); + // find first "non-delimiter". + string::size_type pos = str.find_first_of(",", lastPos); + + while (string::npos != pos || string::npos != lastPos) + { + // found a token, add it to the vector. + tokens.push_back(str.substr(lastPos, pos - lastPos)); + // skip delimiters. Note the "not_of" + lastPos = str.find_first_not_of(",", pos); + // find next "non-delimiter" + pos = str.find_first_of(",", lastPos); + } + return tokens.size(); + } + + public: + ModuleConnJoin(InspIRCd* Me) + : Module(Me) + { + OnRehash(NULL, ""); + } + + Priority Prioritize() + { + return PRIORITY_LAST; + } + + void Implements(char* List) + { + List[I_OnPostConnect] = List[I_OnRehash] = 1; + } + + virtual void OnRehash(userrec* user, const std::string ¶meter) + { + ConfigReader* conf = new ConfigReader(ServerInstance); + JoinChan = conf->ReadValue("autojoin", "channel", 0); + Joinchans.clear(); + if (!JoinChan.empty()) + tokenize(JoinChan,Joinchans); + DELETE(conf); + } + + virtual ~ModuleConnJoin() + { + } + + virtual Version GetVersion() + { + return Version(1,1,0,1,VF_VENDOR,API_VERSION); + } + + virtual void OnPostConnect(userrec* user) + { + if (!IS_LOCAL(user)) + return; + + for(std::vector<std::string>::iterator it = Joinchans.begin(); it != Joinchans.end(); it++) + if (ServerInstance->IsChannel(it->c_str())) + chanrec::JoinUser(ServerInstance, user, it->c_str(), false, "", ServerInstance->Time(true)); + } + +}; + + +MODULE_INIT(ModuleConnJoin) diff --git a/src/modules/m_conn_umodes.cpp b/src/modules/m_conn_umodes.cpp index f9118a384..3f27eeff5 100644 --- a/src/modules/m_conn_umodes.cpp +++ b/src/modules/m_conn_umodes.cpp @@ -1 +1,104 @@ -/* +------------------------------------+
* | 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 "users.h"
#include "channels.h"
#include "modules.h"
#include "wildcard.h"
/* $ModDesc: Sets (and unsets) modes on users when they connect */
class ModuleModesOnConnect : public Module
{
private:
ConfigReader *Conf;
public:
ModuleModesOnConnect(InspIRCd* Me)
: Module(Me)
{
Conf = new ConfigReader(ServerInstance);
}
void Implements(char* List)
{
List[I_OnPostConnect] = List[I_OnRehash] = 1;
}
virtual void OnRehash(userrec* user, const std::string ¶meter)
{
DELETE(Conf);
Conf = new ConfigReader(ServerInstance);
}
virtual ~ModuleModesOnConnect()
{
DELETE(Conf);
}
virtual Version GetVersion()
{
return Version(1,1,0,1,VF_VENDOR,API_VERSION);
}
virtual void OnPostConnect(userrec* user)
{
if (!IS_LOCAL(user))
return;
for (int j = 0; j < Conf->Enumerate("connect"); j++)
{
std::string hostn = Conf->ReadValue("connect","allow",j);
if ((match(user->GetIPString(),hostn.c_str(),true)) || (match(user->host,hostn.c_str())))
{
std::string ThisModes = Conf->ReadValue("connect","modes",j);
if (!ThisModes.empty())
{
std::string buf;
stringstream ss(ThisModes);
vector<string> tokens;
// split ThisUserModes into modes and mode params
while (ss >> buf)
tokens.push_back(buf);
int size = tokens.size() + 1;
const char** modes = new const char*[size];
modes[0] = user->nick;
modes[1] = tokens[0].c_str();
if (tokens.size() > 1)
{
// process mode params
int i = 2;
for (unsigned int k = 1; k < tokens.size(); k++)
{
modes[i] = tokens[k].c_str();
i++;
}
}
ServerInstance->Parser->CallHandler("MODE", modes, size, user);
delete [] modes;
}
break;
}
}
}
};
MODULE_INIT(ModuleModesOnConnect)
\ No newline at end of file +/* +------------------------------------+ + * | 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 "users.h" +#include "channels.h" +#include "modules.h" +#include "wildcard.h" + +/* $ModDesc: Sets (and unsets) modes on users when they connect */ + +class ModuleModesOnConnect : public Module +{ + private: + + ConfigReader *Conf; + + public: + ModuleModesOnConnect(InspIRCd* Me) + : Module(Me) + { + + Conf = new ConfigReader(ServerInstance); + } + + void Implements(char* List) + { + List[I_OnPostConnect] = List[I_OnRehash] = 1; + } + + virtual void OnRehash(userrec* user, const std::string ¶meter) + { + DELETE(Conf); + Conf = new ConfigReader(ServerInstance); + } + + virtual ~ModuleModesOnConnect() + { + DELETE(Conf); + } + + virtual Version GetVersion() + { + return Version(1,1,0,1,VF_VENDOR,API_VERSION); + } + + virtual void OnPostConnect(userrec* user) + { + if (!IS_LOCAL(user)) + return; + + for (int j = 0; j < Conf->Enumerate("connect"); j++) + { + std::string hostn = Conf->ReadValue("connect","allow",j); + if ((match(user->GetIPString(),hostn.c_str(),true)) || (match(user->host,hostn.c_str()))) + { + std::string ThisModes = Conf->ReadValue("connect","modes",j); + if (!ThisModes.empty()) + { + std::string buf; + stringstream ss(ThisModes); + + vector<string> tokens; + + // split ThisUserModes into modes and mode params + while (ss >> buf) + tokens.push_back(buf); + + int size = tokens.size() + 1; + const char** modes = new const char*[size]; + modes[0] = user->nick; + modes[1] = tokens[0].c_str(); + + if (tokens.size() > 1) + { + // process mode params + int i = 2; + for (unsigned int k = 1; k < tokens.size(); k++) + { + modes[i] = tokens[k].c_str(); + i++; + } + } + + ServerInstance->Parser->CallHandler("MODE", modes, size, user); + delete [] modes; + } + break; + } + } + } +}; + +MODULE_INIT(ModuleModesOnConnect) diff --git a/src/modules/m_conn_waitpong.cpp b/src/modules/m_conn_waitpong.cpp index b84533f88..0dd27ddbd 100644 --- a/src/modules/m_conn_waitpong.cpp +++ b/src/modules/m_conn_waitpong.cpp @@ -1 +1,148 @@ -/* +------------------------------------+
* | 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 "users.h"
#include "channels.h"
#include "modules.h"
/* $ModDesc: Forces connecting clients to send a PONG message back to the server before they can complete their connection */
class ModuleWaitPong : public Module
{
bool sendsnotice;
bool killonbadreply;
const char* extenstr;
public:
ModuleWaitPong(InspIRCd* Me)
: Module(Me), extenstr("waitpong_pingstr")
{
OnRehash(NULL,"");
}
virtual void OnRehash(userrec* user, const std::string ¶m)
{
ConfigReader Conf(ServerInstance);
sendsnotice = Conf.ReadFlag("waitpong", "sendsnotice", 0);
if(Conf.GetError() == CONF_VALUE_NOT_FOUND)
sendsnotice = true;
killonbadreply = Conf.ReadFlag("waitpong", "killonbadreply", 0);
if(Conf.GetError() == CONF_VALUE_NOT_FOUND)
killonbadreply = true;
}
void Implements(char* List)
{
List[I_OnUserRegister] = List[I_OnCheckReady] = List[I_OnPreCommand] = List[I_OnRehash] = List[I_OnUserDisconnect] = List[I_OnCleanup] = 1;
}
char* RandString(unsigned int length)
{
unsigned char* out = new unsigned char[length+1];
for(unsigned int i = 0; i < length; i++)
out[i] = ((rand() % 26) + 65);
out[length] = '\0';
return (char*)out;
}
virtual int OnUserRegister(userrec* user)
{
char* pingrpl = RandString(10);
user->Write("PING :%s", pingrpl);
if(sendsnotice)
user->WriteServ("NOTICE %s :*** If you are having problems connecting due to ping timeouts, please type /quote PONG %s or /raw PONG %s now.", user->nick, pingrpl, pingrpl);
user->Extend(extenstr, pingrpl);
return 0;
}
virtual int OnPreCommand(const std::string &command, const char** parameters, int pcnt, userrec* user, bool validated, const std::string &original_line)
{
if(command == "PONG")
{
char* pingrpl;
user->GetExt(extenstr, pingrpl);
if(pingrpl)
{
if(strcmp(pingrpl, parameters[0]) == 0)
{
DELETE(pingrpl);
user->Shrink(extenstr);
return 1;
}
else
{
if(killonbadreply)
userrec::QuitUser(ServerInstance, user, "Incorrect ping reply for registration");
return 1;
}
}
}
return 0;
}
virtual bool OnCheckReady(userrec* user)
{
char* pingrpl;
return (!user->GetExt(extenstr, pingrpl));
}
virtual void OnUserDisconnect(userrec* user)
{
char* pingrpl;
user->GetExt(extenstr, pingrpl);
if(pingrpl)
{
DELETE(pingrpl);
user->Shrink(extenstr);
}
}
virtual void OnCleanup(int target_type, void* item)
{
if(target_type == TYPE_USER)
{
userrec* user = (userrec*)item;
char* pingrpl;
user->GetExt(extenstr, pingrpl);
if(pingrpl)
{
DELETE(pingrpl);
user->Shrink(extenstr);
}
}
}
virtual ~ModuleWaitPong()
{
}
virtual Version GetVersion()
{
return Version(1, 1, 0, 1, VF_VENDOR, API_VERSION);
}
};
MODULE_INIT(ModuleWaitPong)
\ No newline at end of file +/* +------------------------------------+ + * | 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 "users.h" +#include "channels.h" +#include "modules.h" + +/* $ModDesc: Forces connecting clients to send a PONG message back to the server before they can complete their connection */ + +class ModuleWaitPong : public Module +{ + bool sendsnotice; + bool killonbadreply; + const char* extenstr; + + public: + ModuleWaitPong(InspIRCd* Me) + : Module(Me), extenstr("waitpong_pingstr") + { + OnRehash(NULL,""); + } + + virtual void OnRehash(userrec* user, const std::string ¶m) + { + ConfigReader Conf(ServerInstance); + + sendsnotice = Conf.ReadFlag("waitpong", "sendsnotice", 0); + + if(Conf.GetError() == CONF_VALUE_NOT_FOUND) + sendsnotice = true; + + killonbadreply = Conf.ReadFlag("waitpong", "killonbadreply", 0); + + if(Conf.GetError() == CONF_VALUE_NOT_FOUND) + killonbadreply = true; + } + + void Implements(char* List) + { + List[I_OnUserRegister] = List[I_OnCheckReady] = List[I_OnPreCommand] = List[I_OnRehash] = List[I_OnUserDisconnect] = List[I_OnCleanup] = 1; + } + + char* RandString(unsigned int length) + { + unsigned char* out = new unsigned char[length+1]; + for(unsigned int i = 0; i < length; i++) + out[i] = ((rand() % 26) + 65); + out[length] = '\0'; + + return (char*)out; + } + + virtual int OnUserRegister(userrec* user) + { + char* pingrpl = RandString(10); + + user->Write("PING :%s", pingrpl); + + if(sendsnotice) + user->WriteServ("NOTICE %s :*** If you are having problems connecting due to ping timeouts, please type /quote PONG %s or /raw PONG %s now.", user->nick, pingrpl, pingrpl); + + user->Extend(extenstr, pingrpl); + return 0; + } + + virtual int OnPreCommand(const std::string &command, const char** parameters, int pcnt, userrec* user, bool validated, const std::string &original_line) + { + if(command == "PONG") + { + char* pingrpl; + user->GetExt(extenstr, pingrpl); + + if(pingrpl) + { + if(strcmp(pingrpl, parameters[0]) == 0) + { + DELETE(pingrpl); + user->Shrink(extenstr); + return 1; + } + else + { + if(killonbadreply) + userrec::QuitUser(ServerInstance, user, "Incorrect ping reply for registration"); + return 1; + } + } + } + return 0; + } + + virtual bool OnCheckReady(userrec* user) + { + char* pingrpl; + return (!user->GetExt(extenstr, pingrpl)); + } + + virtual void OnUserDisconnect(userrec* user) + { + char* pingrpl; + user->GetExt(extenstr, pingrpl); + + if(pingrpl) + { + DELETE(pingrpl); + user->Shrink(extenstr); + } + } + + virtual void OnCleanup(int target_type, void* item) + { + if(target_type == TYPE_USER) + { + userrec* user = (userrec*)item; + char* pingrpl; + user->GetExt(extenstr, pingrpl); + + if(pingrpl) + { + DELETE(pingrpl); + user->Shrink(extenstr); + } + } + } + + virtual ~ModuleWaitPong() + { + } + + virtual Version GetVersion() + { + return Version(1, 1, 0, 1, VF_VENDOR, API_VERSION); + } + +}; + +MODULE_INIT(ModuleWaitPong) diff --git a/src/modules/m_connflood.cpp b/src/modules/m_connflood.cpp index 71e52fd01..47b19fdf4 100644 --- a/src/modules/m_connflood.cpp +++ b/src/modules/m_connflood.cpp @@ -1 +1,120 @@ -/* +------------------------------------+
* | 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 "users.h"
#include "modules.h"
/* $ModDesc: Connection throttle */
int conns = 0, throttled = 0;
class ModuleConnFlood : public Module
{
private:
int seconds, maxconns, timeout, boot_wait;
time_t first;
std::string quitmsg;
ConfigReader* conf;
public:
ModuleConnFlood(InspIRCd* Me) : Module(Me)
{
InitConf();
}
virtual ~ModuleConnFlood()
{
}
virtual Version GetVersion()
{
return Version(1,1,0,0,VF_VENDOR,API_VERSION);
}
void Implements(char* List)
{
List[I_OnRehash] = List[I_OnUserRegister] = 1;
}
void InitConf()
{
/* read configuration variables */
conf = new ConfigReader(ServerInstance);
/* throttle configuration */
seconds = conf->ReadInteger("connflood", "seconds", 0, true);
maxconns = conf->ReadInteger("connflood", "maxconns", 0, true);
timeout = conf->ReadInteger("connflood", "timeout", 0, true);
quitmsg = conf->ReadValue("connflood", "quitmsg", 0);
/* seconds to wait when the server just booted */
boot_wait = conf->ReadInteger("connflood", "bootwait", 0, true);
first = ServerInstance->Time();
}
virtual int OnUserRegister(userrec* user)
{
time_t next = ServerInstance->Time();
if ((ServerInstance->startup_time + boot_wait) > next)
return 0;
/* time difference between first and latest connection */
time_t tdiff = next - first;
/* increase connection count */
conns++;
if (throttled == 1)
{
if (tdiff > seconds + timeout)
{
/* expire throttle */
throttled = 0;
ServerInstance->WriteOpers("*** Connection throttle deactivated");
return 0;
}
userrec::QuitUser(ServerInstance, user, quitmsg);
return 1;
}
if (tdiff <= seconds)
{
if (conns >= maxconns)
{
throttled = 1;
ServerInstance->WriteOpers("*** Connection throttle activated");
userrec::QuitUser(ServerInstance, user, quitmsg);
return 1;
}
}
else
{
conns = 1;
first = next;
}
return 0;
}
virtual void OnRehash(userrec* user, const std::string ¶meter)
{
InitConf();
}
};
MODULE_INIT(ModuleConnFlood)
\ No newline at end of file +/* +------------------------------------+ + * | 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 "users.h" +#include "modules.h" + +/* $ModDesc: Connection throttle */ + +int conns = 0, throttled = 0; + +class ModuleConnFlood : public Module +{ +private: + int seconds, maxconns, timeout, boot_wait; + time_t first; + std::string quitmsg; + + ConfigReader* conf; + + +public: + ModuleConnFlood(InspIRCd* Me) : Module(Me) + { + + InitConf(); + } + + virtual ~ModuleConnFlood() + { + } + + virtual Version GetVersion() + { + return Version(1,1,0,0,VF_VENDOR,API_VERSION); + } + + void Implements(char* List) + { + List[I_OnRehash] = List[I_OnUserRegister] = 1; + } + + void InitConf() + { + /* read configuration variables */ + conf = new ConfigReader(ServerInstance); + /* throttle configuration */ + seconds = conf->ReadInteger("connflood", "seconds", 0, true); + maxconns = conf->ReadInteger("connflood", "maxconns", 0, true); + timeout = conf->ReadInteger("connflood", "timeout", 0, true); + quitmsg = conf->ReadValue("connflood", "quitmsg", 0); + + /* seconds to wait when the server just booted */ + boot_wait = conf->ReadInteger("connflood", "bootwait", 0, true); + + first = ServerInstance->Time(); + } + + virtual int OnUserRegister(userrec* user) + { + time_t next = ServerInstance->Time(); + + if ((ServerInstance->startup_time + boot_wait) > next) + return 0; + + /* time difference between first and latest connection */ + time_t tdiff = next - first; + + /* increase connection count */ + conns++; + + if (throttled == 1) + { + if (tdiff > seconds + timeout) + { + /* expire throttle */ + throttled = 0; + ServerInstance->WriteOpers("*** Connection throttle deactivated"); + return 0; + } + userrec::QuitUser(ServerInstance, user, quitmsg); + return 1; + } + + if (tdiff <= seconds) + { + if (conns >= maxconns) + { + throttled = 1; + ServerInstance->WriteOpers("*** Connection throttle activated"); + userrec::QuitUser(ServerInstance, user, quitmsg); + return 1; + } + } + else + { + conns = 1; + first = next; + } + return 0; + } + + virtual void OnRehash(userrec* user, const std::string ¶meter) + { + InitConf(); + } + +}; + +MODULE_INIT(ModuleConnFlood) diff --git a/src/modules/m_cycle.cpp b/src/modules/m_cycle.cpp index b1a22941d..eb20f4f99 100644 --- a/src/modules/m_cycle.cpp +++ b/src/modules/m_cycle.cpp @@ -1 +1,99 @@ -/* +------------------------------------+
* | 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 "users.h"
#include "channels.h"
#include "modules.h"
/* $ModDesc: Provides support for unreal-style CYCLE command. */
/** Handle /CYCLE
*/
class cmd_cycle : public command_t
{
public:
cmd_cycle (InspIRCd* Instance) : command_t(Instance,"CYCLE", 0, 1)
{
this->source = "m_cycle.so";
syntax = "<channel> :[reason]";
}
CmdResult Handle (const char** parameters, int pcnt, userrec *user)
{
chanrec* channel = ServerInstance->FindChan(parameters[0]);
std::string reason = ConvToStr("Cycling");
if (pcnt > 1)
{
/* reason provided, use it */
reason = reason + ": " + parameters[1];
}
if (channel)
{
/*
* technically, this is only ever sent locally, but pays to be safe ;p
*/
if (IS_LOCAL(user))
{
if (channel->GetStatus(user) < STATUS_VOICE && channel->IsBanned(user))
{
/* banned, boned. drop the message. */
user->WriteServ("NOTICE "+std::string(user->nick)+" :*** You may not cycle, as you are banned on channel " + channel->name);
return CMD_FAILURE;
}
/* XXX in the future, this may move to a static chanrec method (the delete.) -- w00t */
if (!channel->PartUser(user, reason.c_str()))
delete channel;
chanrec::JoinUser(ServerInstance, user, parameters[0], true, "", ServerInstance->Time(true));
}
return CMD_LOCALONLY;
}
else
{
user->WriteServ("NOTICE %s :*** You are not on this channel", user->nick);
}
return CMD_FAILURE;
}
};
class ModuleCycle : public Module
{
cmd_cycle* mycommand;
public:
ModuleCycle(InspIRCd* Me)
: Module(Me)
{
mycommand = new cmd_cycle(ServerInstance);
ServerInstance->AddCommand(mycommand);
}
virtual ~ModuleCycle()
{
}
virtual Version GetVersion()
{
return Version(1,1,0,1,VF_VENDOR,API_VERSION);
}
};
MODULE_INIT(ModuleCycle)
\ No newline at end of file +/* +------------------------------------+ + * | 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 "users.h" +#include "channels.h" +#include "modules.h" + +/* $ModDesc: Provides support for unreal-style CYCLE command. */ + +/** Handle /CYCLE + */ +class cmd_cycle : public command_t +{ + public: + cmd_cycle (InspIRCd* Instance) : command_t(Instance,"CYCLE", 0, 1) + { + this->source = "m_cycle.so"; + syntax = "<channel> :[reason]"; + } + + CmdResult Handle (const char** parameters, int pcnt, userrec *user) + { + chanrec* channel = ServerInstance->FindChan(parameters[0]); + std::string reason = ConvToStr("Cycling"); + + if (pcnt > 1) + { + /* reason provided, use it */ + reason = reason + ": " + parameters[1]; + } + + if (channel) + { + /* + * technically, this is only ever sent locally, but pays to be safe ;p + */ + if (IS_LOCAL(user)) + { + if (channel->GetStatus(user) < STATUS_VOICE && channel->IsBanned(user)) + { + /* banned, boned. drop the message. */ + user->WriteServ("NOTICE "+std::string(user->nick)+" :*** You may not cycle, as you are banned on channel " + channel->name); + return CMD_FAILURE; + } + + /* XXX in the future, this may move to a static chanrec method (the delete.) -- w00t */ + if (!channel->PartUser(user, reason.c_str())) + delete channel; + + chanrec::JoinUser(ServerInstance, user, parameters[0], true, "", ServerInstance->Time(true)); + } + + return CMD_LOCALONLY; + } + else + { + user->WriteServ("NOTICE %s :*** You are not on this channel", user->nick); + } + + return CMD_FAILURE; + } +}; + + +class ModuleCycle : public Module +{ + cmd_cycle* mycommand; + public: + ModuleCycle(InspIRCd* Me) + : Module(Me) + { + + mycommand = new cmd_cycle(ServerInstance); + ServerInstance->AddCommand(mycommand); + } + + virtual ~ModuleCycle() + { + } + + virtual Version GetVersion() + { + return Version(1,1,0,1,VF_VENDOR,API_VERSION); + } + +}; + +MODULE_INIT(ModuleCycle) diff --git a/src/modules/m_dccallow.cpp b/src/modules/m_dccallow.cpp index 61ef90d89..bfec3c5e1 100644 --- a/src/modules/m_dccallow.cpp +++ b/src/modules/m_dccallow.cpp @@ -1 +1,489 @@ -/* +------------------------------------+
* | 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 "users.h"
#include "channels.h"
#include "modules.h"
/* $ModDesc: Povides support for the /DCCALLOW command */
static ConfigReader *Conf;
class BannedFileList
{
public:
std::string filemask;
std::string action;
};
class DCCAllow
{
public:
std::string nickname;
std::string hostmask;
time_t set_on;
long length;
DCCAllow() { }
DCCAllow(const std::string &nick, const std::string &hm, const time_t so, const long ln) : nickname(nick), hostmask(hm), set_on(so), length(ln) { }
};
typedef std::vector<userrec *> userlist;
userlist ul;
typedef std::vector<DCCAllow> dccallowlist;
dccallowlist* dl;
typedef std::vector<BannedFileList> bannedfilelist;
bannedfilelist bfl;
class cmd_dccallow : public command_t
{
public:
cmd_dccallow(InspIRCd* Me) : command_t(Me, "DCCALLOW", 0, 0)
{
this->source = "m_dccallow.so";
syntax = "{[+|-]<nick> <time>|HELP|LIST}";
}
CmdResult Handle(const char **parameters, int pcnt, userrec *user)
{
/* syntax: DCCALLOW [+|-]<nick> (<time>) */
if (!pcnt)
{
// display current DCCALLOW list
DisplayDCCAllowList(user);
return CMD_FAILURE;
}
else if (pcnt > 0)
{
char action = *parameters[0];
// if they didn't specify an action, this is probably a command
if (action != '+' && action != '-')
{
if (!strcasecmp(parameters[0], "LIST"))
{
// list current DCCALLOW list
DisplayDCCAllowList(user);
return CMD_FAILURE;
}
else if (!strcasecmp(parameters[0], "HELP"))
{
// display help
DisplayHelp(user);
return CMD_FAILURE;
}
}
std::string nick = parameters[0] + 1;
userrec *target = ServerInstance->FindNick(nick);
if (target)
{
if (action == '-')
{
user->GetExt("dccallow_list", dl);
// check if it contains any entries
if (dl)
{
if (dl->size())
{
for (dccallowlist::iterator i = dl->begin(); i != dl->end(); ++i)
{
// search through list
if (i->nickname == target->nick)
{
dl->erase(i);
user->WriteServ("995 %s %s :Removed %s from your DCCALLOW list", user->nick, user->nick, target->nick);
break;
}
}
}
}
else
{
DELETE(dl);
user->Shrink("dccallow_list");
// remove from userlist
for (userlist::iterator j = ul.begin(); j != ul.end(); ++j)
{
userrec* u = (userrec*)(*j);
if (u == user)
{
ul.erase(j);
break;
}
}
}
}
else if (action == '+')
{
// fetch current DCCALLOW list
user->GetExt("dccallow_list", dl);
// they don't have one, create it
if (!dl)
{
dl = new dccallowlist;
user->Extend("dccallow_list", dl);
// add this user to the userlist
ul.push_back(user);
}
for (dccallowlist::const_iterator k = dl->begin(); k != dl->end(); ++k)
{
if (k->nickname == target->nick)
{
user->WriteServ("996 %s %s :%s is already on your DCCALLOW list", user->nick, user->nick, target->nick);
return CMD_FAILURE;
}
else if (ServerInstance->MatchText(user->GetFullHost(), k->hostmask))
{
user->WriteServ("996 %s %s :You cannot add yourself to your own DCCALLOW list!", user->nick, user->nick);
return CMD_FAILURE;
}
}
std::string mask = std::string(target->nick)+"!"+std::string(target->ident)+"@"+std::string(target->dhost);
std::string default_length = Conf->ReadValue("dccallow", "length", 0);
long length;
if (pcnt < 2)
{
length = ServerInstance->Duration(default_length);
}
else if (!atoi(parameters[1]))
{
length = 0;
}
else
{
length = ServerInstance->Duration(parameters[1]);
}
if (!ServerInstance->IsValidMask(mask.c_str()))
{
return CMD_FAILURE;
}
dl->push_back(DCCAllow(target->nick, mask, ServerInstance->Time(), length));
if (length > 0)
{
user->WriteServ("993 %s %s :Added %s to DCCALLOW list for %d seconds", user->nick, user->nick, target->nick, length);
}
else
{
user->WriteServ("994 %s %s :Added %s to DCCALLOW list for this session", user->nick, user->nick, target->nick);
}
/* route it. */
return CMD_SUCCESS;
}
}
else
{
// nick doesn't exist
user->WriteServ("401 %s %s :No such nick/channel", user->nick, nick.c_str());
return CMD_FAILURE;
}
}
return CMD_FAILURE;
}
void DisplayHelp(userrec* user)
{
user->WriteServ("998 %s :DCCALLOW [<+|->nick [time]] [list] [help]", user->nick);
user->WriteServ("998 %s :You may allow DCCs from specific users by specifying a", user->nick);
user->WriteServ("998 %s :DCC allow for the user you want to receive DCCs from.", user->nick);
user->WriteServ("998 %s :For example, to allow the user Brain to send you inspircd.exe", user->nick);
user->WriteServ("998 %s :you would type:", user->nick);
user->WriteServ("998 %s :/DCCALLOW +Brain", user->nick);
user->WriteServ("998 %s :Brain would then be able to send you files. They would have to", user->nick);
user->WriteServ("998 %s :resend the file again if the server gave them an error message", user->nick);
user->WriteServ("998 %s :before you added them to your DCCALLOW list.", user->nick);
user->WriteServ("998 %s :DCCALLOW entries will be temporary by default, if you want to add", user->nick);
user->WriteServ("998 %s :them to your DCCALLOW list until you leave IRC, type:", user->nick);
user->WriteServ("998 %s :/DCCALLOW +Brain 0", user->nick);
user->WriteServ("998 %s :To remove the user from your DCCALLOW list, type:", user->nick);
user->WriteServ("998 %s :/DCCALLOW -Brain", user->nick);
user->WriteServ("998 %s :To see the users in your DCCALLOW list, type:", user->nick);
user->WriteServ("998 %s :/DCCALLOW LIST", user->nick);
user->WriteServ("998 %s :NOTE: If the user leaves IRC or changes their nickname", user->nick);
user->WriteServ("998 %s : they will be removed from your DCCALLOW list.", user->nick);
user->WriteServ("998 %s : your DCCALLOW list will be deleted when you leave IRC.", user->nick);
user->WriteServ("999 %s :End of DCCALLOW HELP", user->nick);
}
void DisplayDCCAllowList(userrec* user)
{
// display current DCCALLOW list
user->WriteServ("990 %s :Users on your DCCALLOW list:", user->nick);
user->GetExt("dccallow_list", dl);
if (dl)
{
for (dccallowlist::const_iterator c = dl->begin(); c != dl->end(); ++c)
{
user->WriteServ("991 %s %s :%s (%s)", user->nick, user->nick, c->nickname.c_str(), c->hostmask.c_str());
}
}
user->WriteServ("992 %s :End of DCCALLOW list", user->nick);
}
};
class ModuleDCCAllow : public Module
{
cmd_dccallow* mycommand;
public:
ModuleDCCAllow(InspIRCd* Me)
: Module(Me)
{
Conf = new ConfigReader(ServerInstance);
mycommand = new cmd_dccallow(ServerInstance);
ServerInstance->AddCommand(mycommand);
ReadFileConf();
}
void Implements(char* List)
{
List[I_OnUserPreMessage] = List[I_OnUserPreNotice] = List[I_OnUserQuit] = List[I_OnUserPreNick] = List[I_OnRehash] = 1;
}
virtual void OnRehash(userrec* user, const std::string ¶meter)
{
delete Conf;
Conf = new ConfigReader(ServerInstance);
}
virtual void OnUserQuit(userrec* user, const std::string &reason, const std::string &oper_message)
{
dccallowlist* dl;
// remove their DCCALLOW list if they have one
user->GetExt("dccallow_list", dl);
if (dl)
{
DELETE(dl);
user->Shrink("dccallow_list");
RemoveFromUserlist(user);
}
// remove them from any DCCALLOW lists
// they are currently on
RemoveNick(user);
}
virtual int OnUserPreNick(userrec* user, const std::string &newnick)
{
RemoveNick(user);
return 0;
}
virtual int OnUserPreMessage(userrec* user, void* dest, int target_type, std::string &text, char status, CUList &exempt_list)
{
return OnUserPreNotice(user, dest, target_type, text, status, exempt_list);
}
virtual int OnUserPreNotice(userrec* user, void* dest, int target_type, std::string &text, char status, CUList &exempt_list)
{
if (!IS_LOCAL(user))
return 0;
if (target_type == TYPE_USER)
{
userrec* u = (userrec*)dest;
/* Always allow a user to dcc themselves (although... why?) */
if (user == u)
return 0;
if ((text.length()) && (text[0] == '\1'))
{
Expire();
// :jamie!jamie@test-D4457903BA652E0F.silverdream.org PRIVMSG eimaj :DCC SEND m_dnsbl.cpp 3232235786 52650 9676
// :jamie!jamie@test-D4457903BA652E0F.silverdream.org PRIVMSG eimaj :VERSION
if (strncmp(text.c_str(), "\1DCC ", 5) == 0)
{
u->GetExt("dccallow_list", dl);
if (dl && dl->size())
{
for (dccallowlist::const_iterator iter = dl->begin(); iter != dl->end(); ++iter)
if (ServerInstance->MatchText(user->GetFullHost(), iter->hostmask))
return 0;
}
// tokenize
std::stringstream ss(text);
std::string buf;
std::vector<std::string> tokens;
while (ss >> buf)
tokens.push_back(buf);
irc::string type = tokens[1].c_str();
bool blockchat = Conf->ReadFlag("dccallow", "blockchat", 0);
if (type == "SEND")
{
std::string defaultaction = Conf->ReadValue("dccallow", "action", 0);
std::string filename = tokens[2];
if (defaultaction == "allow")
return 0;
for (unsigned int i = 0; i < bfl.size(); i++)
{
if (ServerInstance->MatchText(filename, bfl[i].filemask))
{
if (bfl[i].action == "allow")
return 0;
}
else
{
if (defaultaction == "allow")
return 0;
}
user->WriteServ("NOTICE %s :The user %s is not accepting DCC SENDs from you. Your file %s was not sent.", user->nick, u->nick, filename.c_str());
u->WriteServ("NOTICE %s :%s (%s@%s) attempted to send you a file named %s, which was blocked.", u->nick, user->nick, user->ident, user->dhost, filename.c_str());
u->WriteServ("NOTICE %s :If you trust %s and were expecting this, you can type /DCCALLOW HELP for information on the DCCALLOW system.", u->nick, user->nick);
return 1;
}
}
else if ((type == "CHAT") && (blockchat))
{
user->WriteServ("NOTICE %s :The user %s is not accepting DCC CHAT requests from you.", user->nick, u->nick);
u->WriteServ("NOTICE %s :%s (%s@%s) attempted to initiate a DCC CHAT session, which was blocked.", u->nick, user->nick, user->ident, user->dhost);
u->WriteServ("NOTICE %s :If you trust %s and were expecting this, you can type /DCCALLOW HELP for information on the DCCALLOW system.", u->nick, user->nick);
return 1;
}
}
}
}
return 0;
}
void Expire()
{
for (userlist::iterator iter = ul.begin(); iter != ul.end(); ++iter)
{
userrec* u = (userrec*)(*iter);
u->GetExt("dccallow_list", dl);
if (dl)
{
if (dl->size())
{
dccallowlist::iterator iter = dl->begin();
while (iter != dl->end())
{
if ((iter->set_on + iter->length) <= ServerInstance->Time())
{
u->WriteServ("997 %s %s :DCCALLOW entry for %s has expired", u->nick, u->nick, iter->nickname.c_str());
iter = dl->erase(iter);
}
else
{
++iter;
}
}
}
}
else
{
RemoveFromUserlist(u);
}
}
}
void RemoveNick(userrec* user)
{
/* Iterate through all DCCALLOW lists and remove user */
for (userlist::iterator iter = ul.begin(); iter != ul.end(); ++iter)
{
userrec *u = (userrec*)(*iter);
u->GetExt("dccallow_list", dl);
if (dl)
{
if (dl->size())
{
for (dccallowlist::iterator i = dl->begin(); i != dl->end(); ++i)
{
if (i->nickname == user->nick)
{
u->WriteServ("NOTICE %s :%s left the network or changed their nickname and has been removed from your DCCALLOW list", u->nick, i->nickname.c_str());
u->WriteServ("995 %s %s :Removed %s from your DCCALLOW list", u->nick, u->nick, i->nickname.c_str());
dl->erase(i);
break;
}
}
}
}
else
{
RemoveFromUserlist(u);
}
}
}
void RemoveFromUserlist(userrec *user)
{
// remove user from userlist
for (userlist::iterator j = ul.begin(); j != ul.end(); ++j)
{
userrec* u = (userrec*)(*j);
if (u == user)
{
ul.erase(j);
break;
}
}
}
void ReadFileConf()
{
bfl.clear();
for (int i = 0; i < Conf->Enumerate("banfile"); i++)
{
BannedFileList bf;
std::string fileglob = Conf->ReadValue("banfile", "pattern", i);
std::string action = Conf->ReadValue("banfile", "action", i);
bf.filemask = fileglob;
bf.action = action;
bfl.push_back(bf);
}
}
virtual ~ModuleDCCAllow()
{
}
virtual Version GetVersion()
{
return Version(1,1,0,0,VF_COMMON|VF_VENDOR,API_VERSION);
}
};
MODULE_INIT(ModuleDCCAllow)
\ No newline at end of file +/* +------------------------------------+ + * | 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 "users.h" +#include "channels.h" +#include "modules.h" + +/* $ModDesc: Povides support for the /DCCALLOW command */ + +static ConfigReader *Conf; + +class BannedFileList +{ + public: + std::string filemask; + std::string action; +}; + +class DCCAllow +{ + public: + std::string nickname; + std::string hostmask; + time_t set_on; + long length; + + DCCAllow() { } + + DCCAllow(const std::string &nick, const std::string &hm, const time_t so, const long ln) : nickname(nick), hostmask(hm), set_on(so), length(ln) { } +}; + +typedef std::vector<userrec *> userlist; +userlist ul; +typedef std::vector<DCCAllow> dccallowlist; +dccallowlist* dl; +typedef std::vector<BannedFileList> bannedfilelist; +bannedfilelist bfl; + +class cmd_dccallow : public command_t +{ + public: + cmd_dccallow(InspIRCd* Me) : command_t(Me, "DCCALLOW", 0, 0) + { + this->source = "m_dccallow.so"; + syntax = "{[+|-]<nick> <time>|HELP|LIST}"; + } + + CmdResult Handle(const char **parameters, int pcnt, userrec *user) + { + /* syntax: DCCALLOW [+|-]<nick> (<time>) */ + if (!pcnt) + { + // display current DCCALLOW list + DisplayDCCAllowList(user); + return CMD_FAILURE; + } + else if (pcnt > 0) + { + char action = *parameters[0]; + + // if they didn't specify an action, this is probably a command + if (action != '+' && action != '-') + { + if (!strcasecmp(parameters[0], "LIST")) + { + // list current DCCALLOW list + DisplayDCCAllowList(user); + return CMD_FAILURE; + } + else if (!strcasecmp(parameters[0], "HELP")) + { + // display help + DisplayHelp(user); + return CMD_FAILURE; + } + } + + std::string nick = parameters[0] + 1; + userrec *target = ServerInstance->FindNick(nick); + + if (target) + { + + if (action == '-') + { + user->GetExt("dccallow_list", dl); + // check if it contains any entries + if (dl) + { + if (dl->size()) + { + for (dccallowlist::iterator i = dl->begin(); i != dl->end(); ++i) + { + // search through list + if (i->nickname == target->nick) + { + dl->erase(i); + user->WriteServ("995 %s %s :Removed %s from your DCCALLOW list", user->nick, user->nick, target->nick); + break; + } + } + } + } + else + { + DELETE(dl); + user->Shrink("dccallow_list"); + + // remove from userlist + for (userlist::iterator j = ul.begin(); j != ul.end(); ++j) + { + userrec* u = (userrec*)(*j); + if (u == user) + { + ul.erase(j); + break; + } + } + } + } + else if (action == '+') + { + // fetch current DCCALLOW list + user->GetExt("dccallow_list", dl); + // they don't have one, create it + if (!dl) + { + dl = new dccallowlist; + user->Extend("dccallow_list", dl); + // add this user to the userlist + ul.push_back(user); + } + for (dccallowlist::const_iterator k = dl->begin(); k != dl->end(); ++k) + { + if (k->nickname == target->nick) + { + user->WriteServ("996 %s %s :%s is already on your DCCALLOW list", user->nick, user->nick, target->nick); + return CMD_FAILURE; + } + else if (ServerInstance->MatchText(user->GetFullHost(), k->hostmask)) + { + user->WriteServ("996 %s %s :You cannot add yourself to your own DCCALLOW list!", user->nick, user->nick); + return CMD_FAILURE; + } + } + + std::string mask = std::string(target->nick)+"!"+std::string(target->ident)+"@"+std::string(target->dhost); + std::string default_length = Conf->ReadValue("dccallow", "length", 0); + + long length; + if (pcnt < 2) + { + length = ServerInstance->Duration(default_length); + } + else if (!atoi(parameters[1])) + { + length = 0; + } + else + { + length = ServerInstance->Duration(parameters[1]); + } + + if (!ServerInstance->IsValidMask(mask.c_str())) + { + return CMD_FAILURE; + } + + dl->push_back(DCCAllow(target->nick, mask, ServerInstance->Time(), length)); + + if (length > 0) + { + user->WriteServ("993 %s %s :Added %s to DCCALLOW list for %d seconds", user->nick, user->nick, target->nick, length); + } + else + { + user->WriteServ("994 %s %s :Added %s to DCCALLOW list for this session", user->nick, user->nick, target->nick); + } + + /* route it. */ + return CMD_SUCCESS; + } + } + else + { + // nick doesn't exist + user->WriteServ("401 %s %s :No such nick/channel", user->nick, nick.c_str()); + return CMD_FAILURE; + } + } + return CMD_FAILURE; + } + + void DisplayHelp(userrec* user) + { + user->WriteServ("998 %s :DCCALLOW [<+|->nick [time]] [list] [help]", user->nick); + user->WriteServ("998 %s :You may allow DCCs from specific users by specifying a", user->nick); + user->WriteServ("998 %s :DCC allow for the user you want to receive DCCs from.", user->nick); + user->WriteServ("998 %s :For example, to allow the user Brain to send you inspircd.exe", user->nick); + user->WriteServ("998 %s :you would type:", user->nick); + user->WriteServ("998 %s :/DCCALLOW +Brain", user->nick); + user->WriteServ("998 %s :Brain would then be able to send you files. They would have to", user->nick); + user->WriteServ("998 %s :resend the file again if the server gave them an error message", user->nick); + user->WriteServ("998 %s :before you added them to your DCCALLOW list.", user->nick); + user->WriteServ("998 %s :DCCALLOW entries will be temporary by default, if you want to add", user->nick); + user->WriteServ("998 %s :them to your DCCALLOW list until you leave IRC, type:", user->nick); + user->WriteServ("998 %s :/DCCALLOW +Brain 0", user->nick); + user->WriteServ("998 %s :To remove the user from your DCCALLOW list, type:", user->nick); + user->WriteServ("998 %s :/DCCALLOW -Brain", user->nick); + user->WriteServ("998 %s :To see the users in your DCCALLOW list, type:", user->nick); + user->WriteServ("998 %s :/DCCALLOW LIST", user->nick); + user->WriteServ("998 %s :NOTE: If the user leaves IRC or changes their nickname", user->nick); + user->WriteServ("998 %s : they will be removed from your DCCALLOW list.", user->nick); + user->WriteServ("998 %s : your DCCALLOW list will be deleted when you leave IRC.", user->nick); + user->WriteServ("999 %s :End of DCCALLOW HELP", user->nick); + } + + void DisplayDCCAllowList(userrec* user) + { + // display current DCCALLOW list + user->WriteServ("990 %s :Users on your DCCALLOW list:", user->nick); + user->GetExt("dccallow_list", dl); + + if (dl) + { + for (dccallowlist::const_iterator c = dl->begin(); c != dl->end(); ++c) + { + user->WriteServ("991 %s %s :%s (%s)", user->nick, user->nick, c->nickname.c_str(), c->hostmask.c_str()); + } + } + + user->WriteServ("992 %s :End of DCCALLOW list", user->nick); + } + +}; + +class ModuleDCCAllow : public Module +{ + cmd_dccallow* mycommand; + public: + + ModuleDCCAllow(InspIRCd* Me) + : Module(Me) + { + Conf = new ConfigReader(ServerInstance); + mycommand = new cmd_dccallow(ServerInstance); + ServerInstance->AddCommand(mycommand); + ReadFileConf(); + } + + void Implements(char* List) + { + List[I_OnUserPreMessage] = List[I_OnUserPreNotice] = List[I_OnUserQuit] = List[I_OnUserPreNick] = List[I_OnRehash] = 1; + } + + virtual void OnRehash(userrec* user, const std::string ¶meter) + { + delete Conf; + Conf = new ConfigReader(ServerInstance); + } + + virtual void OnUserQuit(userrec* user, const std::string &reason, const std::string &oper_message) + { + dccallowlist* dl; + + // remove their DCCALLOW list if they have one + user->GetExt("dccallow_list", dl); + if (dl) + { + DELETE(dl); + user->Shrink("dccallow_list"); + RemoveFromUserlist(user); + } + + // remove them from any DCCALLOW lists + // they are currently on + RemoveNick(user); + } + + + virtual int OnUserPreNick(userrec* user, const std::string &newnick) + { + RemoveNick(user); + return 0; + } + + virtual int OnUserPreMessage(userrec* user, void* dest, int target_type, std::string &text, char status, CUList &exempt_list) + { + return OnUserPreNotice(user, dest, target_type, text, status, exempt_list); + } + + virtual int OnUserPreNotice(userrec* user, void* dest, int target_type, std::string &text, char status, CUList &exempt_list) + { + if (!IS_LOCAL(user)) + return 0; + + if (target_type == TYPE_USER) + { + userrec* u = (userrec*)dest; + + /* Always allow a user to dcc themselves (although... why?) */ + if (user == u) + return 0; + + if ((text.length()) && (text[0] == '\1')) + { + Expire(); + + // :jamie!jamie@test-D4457903BA652E0F.silverdream.org PRIVMSG eimaj :DCC SEND m_dnsbl.cpp 3232235786 52650 9676 + // :jamie!jamie@test-D4457903BA652E0F.silverdream.org PRIVMSG eimaj :VERSION + + if (strncmp(text.c_str(), "\1DCC ", 5) == 0) + { + u->GetExt("dccallow_list", dl); + + if (dl && dl->size()) + { + for (dccallowlist::const_iterator iter = dl->begin(); iter != dl->end(); ++iter) + if (ServerInstance->MatchText(user->GetFullHost(), iter->hostmask)) + return 0; + } + + // tokenize + std::stringstream ss(text); + std::string buf; + std::vector<std::string> tokens; + + while (ss >> buf) + tokens.push_back(buf); + + irc::string type = tokens[1].c_str(); + + bool blockchat = Conf->ReadFlag("dccallow", "blockchat", 0); + + if (type == "SEND") + { + std::string defaultaction = Conf->ReadValue("dccallow", "action", 0); + std::string filename = tokens[2]; + + if (defaultaction == "allow") + return 0; + + for (unsigned int i = 0; i < bfl.size(); i++) + { + if (ServerInstance->MatchText(filename, bfl[i].filemask)) + { + if (bfl[i].action == "allow") + return 0; + } + else + { + if (defaultaction == "allow") + return 0; + } + user->WriteServ("NOTICE %s :The user %s is not accepting DCC SENDs from you. Your file %s was not sent.", user->nick, u->nick, filename.c_str()); + u->WriteServ("NOTICE %s :%s (%s@%s) attempted to send you a file named %s, which was blocked.", u->nick, user->nick, user->ident, user->dhost, filename.c_str()); + u->WriteServ("NOTICE %s :If you trust %s and were expecting this, you can type /DCCALLOW HELP for information on the DCCALLOW system.", u->nick, user->nick); + return 1; + } + } + else if ((type == "CHAT") && (blockchat)) + { + user->WriteServ("NOTICE %s :The user %s is not accepting DCC CHAT requests from you.", user->nick, u->nick); + u->WriteServ("NOTICE %s :%s (%s@%s) attempted to initiate a DCC CHAT session, which was blocked.", u->nick, user->nick, user->ident, user->dhost); + u->WriteServ("NOTICE %s :If you trust %s and were expecting this, you can type /DCCALLOW HELP for information on the DCCALLOW system.", u->nick, user->nick); + return 1; + } + } + } + } + return 0; + } + + void Expire() + { + for (userlist::iterator iter = ul.begin(); iter != ul.end(); ++iter) + { + userrec* u = (userrec*)(*iter); + u->GetExt("dccallow_list", dl); + + if (dl) + { + if (dl->size()) + { + dccallowlist::iterator iter = dl->begin(); + while (iter != dl->end()) + { + if ((iter->set_on + iter->length) <= ServerInstance->Time()) + { + u->WriteServ("997 %s %s :DCCALLOW entry for %s has expired", u->nick, u->nick, iter->nickname.c_str()); + iter = dl->erase(iter); + } + else + { + ++iter; + } + } + } + } + else + { + RemoveFromUserlist(u); + } + } + } + + void RemoveNick(userrec* user) + { + /* Iterate through all DCCALLOW lists and remove user */ + for (userlist::iterator iter = ul.begin(); iter != ul.end(); ++iter) + { + userrec *u = (userrec*)(*iter); + u->GetExt("dccallow_list", dl); + + if (dl) + { + if (dl->size()) + { + for (dccallowlist::iterator i = dl->begin(); i != dl->end(); ++i) + { + if (i->nickname == user->nick) + { + + u->WriteServ("NOTICE %s :%s left the network or changed their nickname and has been removed from your DCCALLOW list", u->nick, i->nickname.c_str()); + u->WriteServ("995 %s %s :Removed %s from your DCCALLOW list", u->nick, u->nick, i->nickname.c_str()); + dl->erase(i); + break; + } + } + } + } + else + { + RemoveFromUserlist(u); + } + } + } + + void RemoveFromUserlist(userrec *user) + { + // remove user from userlist + for (userlist::iterator j = ul.begin(); j != ul.end(); ++j) + { + userrec* u = (userrec*)(*j); + if (u == user) + { + ul.erase(j); + break; + } + } + } + + void ReadFileConf() + { + bfl.clear(); + for (int i = 0; i < Conf->Enumerate("banfile"); i++) + { + BannedFileList bf; + std::string fileglob = Conf->ReadValue("banfile", "pattern", i); + std::string action = Conf->ReadValue("banfile", "action", i); + bf.filemask = fileglob; + bf.action = action; + bfl.push_back(bf); + } + + } + + virtual ~ModuleDCCAllow() + { + } + + virtual Version GetVersion() + { + return Version(1,1,0,0,VF_COMMON|VF_VENDOR,API_VERSION); + } +}; + +MODULE_INIT(ModuleDCCAllow) diff --git a/src/modules/m_deaf.cpp b/src/modules/m_deaf.cpp index d9681010d..ad8b31714 100644 --- a/src/modules/m_deaf.cpp +++ b/src/modules/m_deaf.cpp @@ -1 +1,135 @@ -/* +------------------------------------+
* | 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 "users.h"
#include "channels.h"
#include "modules.h"
/* $ModDesc: Provides support for ircu style usermode +d (deaf to channel messages and channel notices) */
/** User mode +d - filter out channel messages and channel notices
*/
class User_d : public ModeHandler
{
public:
User_d(InspIRCd* Instance) : ModeHandler(Instance, 'd', 0, 0, false, MODETYPE_USER, false) { }
ModeAction OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string ¶meter, bool adding)
{
if (adding)
{
if (!dest->IsModeSet('d'))
{
dest->WriteServ("NOTICE %s :*** You have enabled usermode +d, deaf mode. This mode means you WILL NOT receive any messages from any channels you are in. If you did NOT mean to do this, use /mode %s -d.", dest->nick, dest->nick);
dest->SetMode('d',true);
return MODEACTION_ALLOW;
}
}
else
{
if (dest->IsModeSet('d'))
{
dest->SetMode('d',false);
return MODEACTION_ALLOW;
}
}
return MODEACTION_DENY;
}
};
class ModuleDeaf : public Module
{
User_d* m1;
public:
ModuleDeaf(InspIRCd* Me)
: Module(Me)
{
m1 = new User_d(ServerInstance);
if (!ServerInstance->AddMode(m1, 'd'))
throw ModuleException("Could not add new modes!");
}
void Implements(char* List)
{
List[I_OnUserPreMessage] = List[I_OnUserPreNotice] = List[I_OnBuildExemptList] = 1;
}
virtual int OnUserPreNotice(userrec* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list)
{
return PreText(user, dest, target_type, text, status, exempt_list);
}
virtual int OnUserPreMessage(userrec* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list)
{
return PreText(user, dest, target_type, text, status, exempt_list);
}
virtual void OnBuildExemptList(MessageType message_type, chanrec* chan, userrec* sender, char status, CUList &exempt_list)
{
CUList *ulist;
switch (status)
{
case '@':
ulist = chan->GetOppedUsers();
break;
case '%':
ulist = chan->GetHalfoppedUsers();
break;
case '+':
ulist = chan->GetVoicedUsers();
break;
default:
ulist = chan->GetUsers();
break;
}
for (CUList::iterator i = ulist->begin(); i != ulist->end(); i++)
{
if (IS_LOCAL(i->first))
{
if (i->first->IsModeSet('d'))
{
exempt_list[i->first] = i->first->nick;
}
}
}
}
virtual int PreText(userrec* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list)
{
if ((target_type == TYPE_CHANNEL) & (IS_LOCAL(user)))
{
chanrec* chan = (chanrec*)dest;
if (chan)
{
this->OnBuildExemptList(MSG_PRIVMSG, chan, user, status, exempt_list);
}
}
return 0;
}
virtual ~ModuleDeaf()
{
ServerInstance->Modes->DelMode(m1);
DELETE(m1);
}
virtual Version GetVersion()
{
return Version(1,1,0,0,VF_COMMON|VF_VENDOR,API_VERSION);
}
};
MODULE_INIT(ModuleDeaf)
\ No newline at end of file +/* +------------------------------------+ + * | 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 "users.h" +#include "channels.h" +#include "modules.h" + +/* $ModDesc: Provides support for ircu style usermode +d (deaf to channel messages and channel notices) */ + +/** User mode +d - filter out channel messages and channel notices + */ +class User_d : public ModeHandler +{ + public: + User_d(InspIRCd* Instance) : ModeHandler(Instance, 'd', 0, 0, false, MODETYPE_USER, false) { } + + ModeAction OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string ¶meter, bool adding) + { + if (adding) + { + if (!dest->IsModeSet('d')) + { + dest->WriteServ("NOTICE %s :*** You have enabled usermode +d, deaf mode. This mode means you WILL NOT receive any messages from any channels you are in. If you did NOT mean to do this, use /mode %s -d.", dest->nick, dest->nick); + dest->SetMode('d',true); + return MODEACTION_ALLOW; + } + } + else + { + if (dest->IsModeSet('d')) + { + dest->SetMode('d',false); + return MODEACTION_ALLOW; + } + } + return MODEACTION_DENY; + } +}; + +class ModuleDeaf : public Module +{ + User_d* m1; + public: + ModuleDeaf(InspIRCd* Me) + : Module(Me) + { + m1 = new User_d(ServerInstance); + if (!ServerInstance->AddMode(m1, 'd')) + throw ModuleException("Could not add new modes!"); + } + + void Implements(char* List) + { + List[I_OnUserPreMessage] = List[I_OnUserPreNotice] = List[I_OnBuildExemptList] = 1; + } + + virtual int OnUserPreNotice(userrec* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list) + { + return PreText(user, dest, target_type, text, status, exempt_list); + } + + virtual int OnUserPreMessage(userrec* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list) + { + return PreText(user, dest, target_type, text, status, exempt_list); + } + + virtual void OnBuildExemptList(MessageType message_type, chanrec* chan, userrec* sender, char status, CUList &exempt_list) + { + CUList *ulist; + switch (status) + { + case '@': + ulist = chan->GetOppedUsers(); + break; + case '%': + ulist = chan->GetHalfoppedUsers(); + break; + case '+': + ulist = chan->GetVoicedUsers(); + break; + default: + ulist = chan->GetUsers(); + break; + } + + for (CUList::iterator i = ulist->begin(); i != ulist->end(); i++) + { + if (IS_LOCAL(i->first)) + { + if (i->first->IsModeSet('d')) + { + exempt_list[i->first] = i->first->nick; + } + } + } + } + + virtual int PreText(userrec* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list) + { + if ((target_type == TYPE_CHANNEL) & (IS_LOCAL(user))) + { + chanrec* chan = (chanrec*)dest; + if (chan) + { + this->OnBuildExemptList(MSG_PRIVMSG, chan, user, status, exempt_list); + } + } + return 0; + } + + virtual ~ModuleDeaf() + { + ServerInstance->Modes->DelMode(m1); + DELETE(m1); + } + + virtual Version GetVersion() + { + return Version(1,1,0,0,VF_COMMON|VF_VENDOR,API_VERSION); + } + +}; + +MODULE_INIT(ModuleDeaf) diff --git a/src/modules/m_denychans.cpp b/src/modules/m_denychans.cpp index d09e04766..4a01faa7c 100644 --- a/src/modules/m_denychans.cpp +++ b/src/modules/m_denychans.cpp @@ -1 +1,80 @@ -/* +------------------------------------+
* | 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 "users.h"
#include "channels.h"
#include "modules.h"
#include "hashcomp.h"
#include "wildcard.h"
/* $ModDesc: Implements config tags which allow blocking of joins to channels */
class ModuleDenyChannels : public Module
{
private:
ConfigReader *Conf;
public:
ModuleDenyChannels(InspIRCd* Me) : Module(Me)
{
Conf = new ConfigReader(ServerInstance);
}
virtual void OnRehash(userrec* user, const std::string ¶m)
{
DELETE(Conf);
Conf = new ConfigReader(ServerInstance);
}
virtual ~ModuleDenyChannels()
{
DELETE(Conf);
}
virtual Version GetVersion()
{
return Version(1,1,0,1,VF_VENDOR,API_VERSION);
}
void Implements(char* List)
{
List[I_OnUserPreJoin] = List[I_OnRehash] = 1;
}
virtual int OnUserPreJoin(userrec* user, chanrec* chan, const char* cname, std::string &privs)
{
for (int j =0; j < Conf->Enumerate("badchan"); j++)
{
if (match(cname, Conf->ReadValue("badchan","name",j).c_str()))
{
if (IS_OPER(user) && Conf->ReadFlag("badchan","allowopers",j))
{
return 0;
}
else
{
std::string reason = Conf->ReadValue("badchan","reason",j);
user->WriteServ("926 %s %s :Channel %s is forbidden: %s",user->nick,cname,cname,reason.c_str());
return 1;
}
}
}
return 0;
}
};
MODULE_INIT(ModuleDenyChannels)
\ No newline at end of file +/* +------------------------------------+ + * | 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 "users.h" +#include "channels.h" +#include "modules.h" +#include "hashcomp.h" +#include "wildcard.h" + +/* $ModDesc: Implements config tags which allow blocking of joins to channels */ + +class ModuleDenyChannels : public Module +{ + private: + + + ConfigReader *Conf; + + public: + ModuleDenyChannels(InspIRCd* Me) : Module(Me) + { + + Conf = new ConfigReader(ServerInstance); + } + + virtual void OnRehash(userrec* user, const std::string ¶m) + { + DELETE(Conf); + Conf = new ConfigReader(ServerInstance); + } + + virtual ~ModuleDenyChannels() + { + DELETE(Conf); + } + + virtual Version GetVersion() + { + return Version(1,1,0,1,VF_VENDOR,API_VERSION); + } + + void Implements(char* List) + { + List[I_OnUserPreJoin] = List[I_OnRehash] = 1; + } + + virtual int OnUserPreJoin(userrec* user, chanrec* chan, const char* cname, std::string &privs) + { + for (int j =0; j < Conf->Enumerate("badchan"); j++) + { + if (match(cname, Conf->ReadValue("badchan","name",j).c_str())) + { + if (IS_OPER(user) && Conf->ReadFlag("badchan","allowopers",j)) + { + return 0; + } + else + { + std::string reason = Conf->ReadValue("badchan","reason",j); + user->WriteServ("926 %s %s :Channel %s is forbidden: %s",user->nick,cname,cname,reason.c_str()); + return 1; + } + } + } + return 0; + } +}; + +MODULE_INIT(ModuleDenyChannels) diff --git a/src/modules/m_devoice.cpp b/src/modules/m_devoice.cpp index e760db859..e2ada413b 100644 --- a/src/modules/m_devoice.cpp +++ b/src/modules/m_devoice.cpp @@ -1 +1,81 @@ -/* +------------------------------------+
* | 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.
*
* ---------------------------------------------------
*/
/*
* DEVOICE module for InspIRCd
* Syntax: /DEVOICE <#chan>
*/
/* $ModDesc: Provides voiced users with the ability to devoice themselves. */
#include "inspircd.h"
#include "users.h"
#include "channels.h"
#include "modules.h"
/** Handle /DEVOICE
*/
class cmd_devoice : public command_t
{
public:
cmd_devoice (InspIRCd* Instance) : command_t(Instance,"DEVOICE", 0, 1)
{
this->source = "m_devoice.so";
syntax = "<channel>";
}
CmdResult Handle (const char** parameters, int pcnt, userrec *user)
{
chanrec* c = ServerInstance->FindChan(parameters[0]);
if (c && c->HasUser(user))
{
const char* modes[3];
modes[0] = parameters[0];
modes[1] = "-v";
modes[2] = user->nick;
userrec* n = new userrec(ServerInstance);
n->SetFd(FD_MAGIC_NUMBER);
ServerInstance->SendMode(modes,3,n);
delete n;
/* route it */
return CMD_SUCCESS;
}
return CMD_FAILURE;
}
};
class ModuleDeVoice : public Module
{
cmd_devoice *mycommand;
public:
ModuleDeVoice(InspIRCd* Me) : Module(Me)
{
mycommand = new cmd_devoice(ServerInstance);
ServerInstance->AddCommand(mycommand);
}
virtual ~ModuleDeVoice()
{
}
virtual Version GetVersion()
{
return Version(1, 1, 0, 0, VF_VENDOR, API_VERSION);
}
};
MODULE_INIT(ModuleDeVoice)
\ No newline at end of file +/* +------------------------------------+ + * | 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. + * + * --------------------------------------------------- + */ + +/* + * DEVOICE module for InspIRCd + * Syntax: /DEVOICE <#chan> + */ + +/* $ModDesc: Provides voiced users with the ability to devoice themselves. */ + +#include "inspircd.h" +#include "users.h" +#include "channels.h" +#include "modules.h" + +/** Handle /DEVOICE + */ +class cmd_devoice : public command_t +{ + public: + cmd_devoice (InspIRCd* Instance) : command_t(Instance,"DEVOICE", 0, 1) + { + this->source = "m_devoice.so"; + syntax = "<channel>"; + } + + CmdResult Handle (const char** parameters, int pcnt, userrec *user) + { + chanrec* c = ServerInstance->FindChan(parameters[0]); + if (c && c->HasUser(user)) + { + const char* modes[3]; + modes[0] = parameters[0]; + modes[1] = "-v"; + modes[2] = user->nick; + + userrec* n = new userrec(ServerInstance); + n->SetFd(FD_MAGIC_NUMBER); + ServerInstance->SendMode(modes,3,n); + delete n; + + /* route it */ + return CMD_SUCCESS; + } + + return CMD_FAILURE; + } +}; + +class ModuleDeVoice : public Module +{ + cmd_devoice *mycommand; + public: + ModuleDeVoice(InspIRCd* Me) : Module(Me) + { + + mycommand = new cmd_devoice(ServerInstance); + ServerInstance->AddCommand(mycommand); + } + + virtual ~ModuleDeVoice() + { + } + + virtual Version GetVersion() + { + return Version(1, 1, 0, 0, VF_VENDOR, API_VERSION); + } +}; + +MODULE_INIT(ModuleDeVoice) diff --git a/src/modules/m_dnsbl.cpp b/src/modules/m_dnsbl.cpp index 87e9a2cba..d07b268f7 100644 --- a/src/modules/m_dnsbl.cpp +++ b/src/modules/m_dnsbl.cpp @@ -1 +1,353 @@ -/* +------------------------------------+
* | 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 "xline.h"
#include "dns.h"
#include "users.h"
#include "channels.h"
#include "modules.h"
#ifndef WINDOWS
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#endif
/* $ModDesc: Provides handling of DNS blacklists */
/* Class holding data for a single entry */
class DNSBLConfEntry
{
public:
enum EnumBanaction { I_UNKNOWN, I_KILL, I_ZLINE, I_KLINE, I_GLINE };
std::string name, domain, reason;
EnumBanaction banaction;
long duration;
int bitmask;
unsigned long stats_hits, stats_misses;
DNSBLConfEntry(): duration(86400),bitmask(0),stats_hits(0), stats_misses(0) {}
~DNSBLConfEntry() { }
};
/** Resolver for CGI:IRC hostnames encoded in ident/GECOS
*/
class DNSBLResolver : public Resolver
{
int theirfd;
userrec* them;
DNSBLConfEntry *ConfEntry;
public:
DNSBLResolver(Module *me, InspIRCd *ServerInstance, const std::string &hostname, userrec* u, int userfd, DNSBLConfEntry *conf, bool &cached)
: Resolver(ServerInstance, hostname, DNS_QUERY_A, cached, me)
{
theirfd = userfd;
them = u;
ConfEntry = conf;
}
virtual void OnLookupComplete(const std::string &result, unsigned int ttl, bool cached)
{
/* Check the user still exists */
if ((them) && (them == ServerInstance->SE->GetRef(theirfd)))
{
// Now we calculate the bitmask: 256*(256*(256*a+b)+c)+d
if(result.length())
{
unsigned int bitmask = 0;
bool show = false;
in_addr resultip;
/* Convert the result to an in_addr (we can gaurantee we got ipv4)
* Whoever did the loop that was here before, I AM CONFISCATING
* YOUR CRACKPIPE. you know who you are. -- Brain
*/
inet_aton(result.c_str(), &resultip);
bitmask = resultip.s_addr >> 24; /* Last octet (network byte order */
bitmask &= ConfEntry->bitmask;
if (bitmask != 0)
{
std::string reason = ConfEntry->reason;
std::string::size_type x = reason.find("%ip%");
while (x != std::string::npos)
{
reason.erase(x, 4);
reason.insert(x, them->GetIPString());
x = reason.find("%ip%");
}
ConfEntry->stats_hits++;
switch (ConfEntry->banaction)
{
case DNSBLConfEntry::I_KILL:
{
userrec::QuitUser(ServerInstance, them, std::string("Killed (") + reason + ")");
break;
}
case DNSBLConfEntry::I_KLINE:
{
std::string ban = std::string("*@") + them->GetIPString();
if (show)
ServerInstance->XLines->apply_lines(APPLY_KLINES);
show = ServerInstance->XLines->add_kline(ConfEntry->duration, ServerInstance->Config->ServerName, reason.c_str(), ban.c_str());
FOREACH_MOD(I_OnAddKLine,OnAddKLine(ConfEntry->duration, NULL, reason, ban));
break;
}
case DNSBLConfEntry::I_GLINE:
{
std::string ban = std::string("*@") + them->GetIPString();
show = ServerInstance->XLines->add_gline(ConfEntry->duration, ServerInstance->Config->ServerName, reason.c_str(), ban.c_str());
if (show)
ServerInstance->XLines->apply_lines(APPLY_GLINES);
FOREACH_MOD(I_OnAddGLine,OnAddGLine(ConfEntry->duration, NULL, reason, ban));
break;
}
case DNSBLConfEntry::I_ZLINE:
{
show = ServerInstance->XLines->add_zline(ConfEntry->duration, ServerInstance->Config->ServerName, reason.c_str(), them->GetIPString());
if (show)
ServerInstance->XLines->apply_lines(APPLY_ZLINES);
FOREACH_MOD(I_OnAddZLine,OnAddZLine(ConfEntry->duration, NULL, reason, them->GetIPString()));
break;
}
case DNSBLConfEntry::I_UNKNOWN:
{
break;
}
break;
}
if (show)
{
ServerInstance->WriteOpers("*** Connecting user %s detected as being on a DNS blacklist (%s) with result %d", them->GetFullRealHost(), ConfEntry->name.c_str(), bitmask);
}
}
else
ConfEntry->stats_misses++;
}
else
ConfEntry->stats_misses++;
}
}
virtual void OnError(ResolverError e, const std::string &errormessage)
{
}
virtual ~DNSBLResolver()
{
}
};
class ModuleDNSBL : public Module
{
private:
std::vector<DNSBLConfEntry *> DNSBLConfEntries;
/*
* Convert a string to EnumBanaction
*/
DNSBLConfEntry::EnumBanaction str2banaction(const std::string &action)
{
if(action.compare("KILL")==0)
return DNSBLConfEntry::I_KILL;
if(action.compare("KLINE")==0)
return DNSBLConfEntry::I_KLINE;
if(action.compare("ZLINE")==0)
return DNSBLConfEntry::I_ZLINE;
if(action.compare("GLINE")==0)
return DNSBLConfEntry::I_GLINE;
return DNSBLConfEntry::I_UNKNOWN;
}
public:
ModuleDNSBL(InspIRCd *Me) : Module(Me)
{
ReadConf();
}
virtual ~ModuleDNSBL()
{
ClearEntries();
}
virtual Version GetVersion()
{
return Version(2, 0, 0, 1, VF_VENDOR, API_VERSION);
}
void Implements(char* List)
{
List[I_OnRehash] = List[I_OnUserRegister] = List[I_OnStats] = 1;
}
/** Clear entries and free the mem it was using
*/
void ClearEntries()
{
std::vector<DNSBLConfEntry *>::iterator i;
for (std::vector<DNSBLConfEntry *>::iterator i = DNSBLConfEntries.begin(); i != DNSBLConfEntries.end(); i++)
delete *i;
DNSBLConfEntries.clear();
}
/** Fill our conf vector with data
*/
virtual void ReadConf()
{
ConfigReader *MyConf = new ConfigReader(ServerInstance);
ClearEntries();
for (int i=0; i< MyConf->Enumerate("dnsbl"); i++)
{
DNSBLConfEntry *e = new DNSBLConfEntry();
e->name = MyConf->ReadValue("dnsbl", "name", i);
e->reason = MyConf->ReadValue("dnsbl", "reason", i);
e->domain = MyConf->ReadValue("dnsbl", "domain", i);
e->banaction = str2banaction(MyConf->ReadValue("dnsbl", "action", i));
e->duration = ServerInstance->Duration(MyConf->ReadValue("dnsbl", "duration", i));
e->bitmask = MyConf->ReadInteger("dnsbl", "bitmask", i, false);
/* yeah, logic here is a little messy */
if (e->bitmask <= 0)
{
ServerInstance->WriteOpers("*** DNSBL(#%d): invalid bitmask",i);
}
else if (e->name.empty())
{
ServerInstance->WriteOpers("*** DNSBL(#%d): Invalid name",i);
}
else if (e->domain.empty())
{
ServerInstance->WriteOpers("*** DNSBL(#%d): Invalid domain",i);
}
else if (e->banaction == DNSBLConfEntry::I_UNKNOWN)
{
ServerInstance->WriteOpers("*** DNSBL(#%d): Invalid banaction", i);
}
else
{
if (e->reason.empty())
{
ServerInstance->WriteOpers("*** DNSBL(#%d): empty reason, using defaults",i);
e->reason = "Your IP has been blacklisted.";
}
/* add it, all is ok */
DNSBLConfEntries.push_back(e);
continue;
}
/* delete and drop it, error somewhere */
delete e;
}
delete MyConf;
}
virtual void OnRehash(userrec* user, const std::string ¶meter)
{
ReadConf();
}
virtual int OnUserRegister(userrec* user)
{
/* only do lookups on local users */
if (IS_LOCAL(user))
{
/* following code taken from bopm, reverses an IP address. */
struct in_addr in;
unsigned char a, b, c, d;
char reversedipbuf[128];
std::string reversedip;
bool success = false;
if (!inet_aton(user->GetIPString(), &in))
{
#ifdef IPV6
/* We could have an ipv6 address here */
std::string x = user->GetIPString();
/* Is it a 4in6 address? (Compensate for this kernel kludge that people love) */
if (x.find("0::ffff:") == 0)
{
x.erase(x.begin(), x.begin() + 8);
if (inet_aton(x.c_str(), &in))
success = true;
}
#endif
}
else
{
success = true;
}
if (!success)
return 0;
d = (unsigned char) (in.s_addr >> 24) & 0xFF;
c = (unsigned char) (in.s_addr >> 16) & 0xFF;
b = (unsigned char) (in.s_addr >> 8) & 0xFF;
a = (unsigned char) in.s_addr & 0xFF;
snprintf(reversedipbuf, 128, "%d.%d.%d.%d", d, c, b, a);
reversedip = std::string(reversedipbuf);
// For each DNSBL, we will run through this lookup
for (std::vector<DNSBLConfEntry *>::iterator i = DNSBLConfEntries.begin(); i != DNSBLConfEntries.end(); i++)
{
// Fill hostname with a dnsbl style host (d.c.b.a.domain.tld)
std::string hostname = reversedip + "." + (*i)->domain;
/* now we'd need to fire off lookups for `hostname'. */
bool cached;
DNSBLResolver *r = new DNSBLResolver(this, ServerInstance, hostname, user, user->GetFd(), *i, cached);
ServerInstance->AddResolver(r, cached);
}
}
/* don't do anything with this hot potato */
return 0;
}
virtual int OnStats(char symbol, userrec* user, string_list &results)
{
if (symbol != 'd')
return 0;
unsigned long total_hits = 0, total_misses = 0;
for (std::vector<DNSBLConfEntry*>::iterator i = DNSBLConfEntries.begin(); i != DNSBLConfEntries.end(); i++)
{
total_hits += (*i)->stats_hits;
total_misses += (*i)->stats_misses;
results.push_back(std::string(ServerInstance->Config->ServerName) + " 304 " + user->nick + " :DNSBLSTATS DNSbl \"" + (*i)->name + "\" had " +
ConvToStr((*i)->stats_hits) + " hits and " + ConvToStr((*i)->stats_misses) + " misses");
}
results.push_back(std::string(ServerInstance->Config->ServerName) + " 304 " + user->nick + " :DNSBLSTATS Total hits: " + ConvToStr(total_hits));
results.push_back(std::string(ServerInstance->Config->ServerName) + " 304 " + user->nick + " :DNSBLSTATS Total misses: " + ConvToStr(total_misses));
return 0;
}
};
MODULE_INIT(ModuleDNSBL)
\ No newline at end of file +/* +------------------------------------+ + * | 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 "xline.h" +#include "dns.h" +#include "users.h" +#include "channels.h" +#include "modules.h" + +#ifndef WINDOWS +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#endif + +/* $ModDesc: Provides handling of DNS blacklists */ + +/* Class holding data for a single entry */ +class DNSBLConfEntry +{ + public: + enum EnumBanaction { I_UNKNOWN, I_KILL, I_ZLINE, I_KLINE, I_GLINE }; + std::string name, domain, reason; + EnumBanaction banaction; + long duration; + int bitmask; + unsigned long stats_hits, stats_misses; + DNSBLConfEntry(): duration(86400),bitmask(0),stats_hits(0), stats_misses(0) {} + ~DNSBLConfEntry() { } +}; + + +/** Resolver for CGI:IRC hostnames encoded in ident/GECOS + */ +class DNSBLResolver : public Resolver +{ + int theirfd; + userrec* them; + DNSBLConfEntry *ConfEntry; + + public: + + DNSBLResolver(Module *me, InspIRCd *ServerInstance, const std::string &hostname, userrec* u, int userfd, DNSBLConfEntry *conf, bool &cached) + : Resolver(ServerInstance, hostname, DNS_QUERY_A, cached, me) + { + theirfd = userfd; + them = u; + ConfEntry = conf; + } + + virtual void OnLookupComplete(const std::string &result, unsigned int ttl, bool cached) + { + /* Check the user still exists */ + if ((them) && (them == ServerInstance->SE->GetRef(theirfd))) + { + // Now we calculate the bitmask: 256*(256*(256*a+b)+c)+d + if(result.length()) + { + unsigned int bitmask = 0; + bool show = false; + in_addr resultip; + + /* Convert the result to an in_addr (we can gaurantee we got ipv4) + * Whoever did the loop that was here before, I AM CONFISCATING + * YOUR CRACKPIPE. you know who you are. -- Brain + */ + inet_aton(result.c_str(), &resultip); + bitmask = resultip.s_addr >> 24; /* Last octet (network byte order */ + + bitmask &= ConfEntry->bitmask; + + if (bitmask != 0) + { + std::string reason = ConfEntry->reason; + std::string::size_type x = reason.find("%ip%"); + while (x != std::string::npos) + { + reason.erase(x, 4); + reason.insert(x, them->GetIPString()); + x = reason.find("%ip%"); + } + + ConfEntry->stats_hits++; + + switch (ConfEntry->banaction) + { + case DNSBLConfEntry::I_KILL: + { + userrec::QuitUser(ServerInstance, them, std::string("Killed (") + reason + ")"); + break; + } + case DNSBLConfEntry::I_KLINE: + { + std::string ban = std::string("*@") + them->GetIPString(); + if (show) + ServerInstance->XLines->apply_lines(APPLY_KLINES); + show = ServerInstance->XLines->add_kline(ConfEntry->duration, ServerInstance->Config->ServerName, reason.c_str(), ban.c_str()); + FOREACH_MOD(I_OnAddKLine,OnAddKLine(ConfEntry->duration, NULL, reason, ban)); + break; + } + case DNSBLConfEntry::I_GLINE: + { + std::string ban = std::string("*@") + them->GetIPString(); + show = ServerInstance->XLines->add_gline(ConfEntry->duration, ServerInstance->Config->ServerName, reason.c_str(), ban.c_str()); + if (show) + ServerInstance->XLines->apply_lines(APPLY_GLINES); + FOREACH_MOD(I_OnAddGLine,OnAddGLine(ConfEntry->duration, NULL, reason, ban)); + break; + } + case DNSBLConfEntry::I_ZLINE: + { + show = ServerInstance->XLines->add_zline(ConfEntry->duration, ServerInstance->Config->ServerName, reason.c_str(), them->GetIPString()); + if (show) + ServerInstance->XLines->apply_lines(APPLY_ZLINES); + FOREACH_MOD(I_OnAddZLine,OnAddZLine(ConfEntry->duration, NULL, reason, them->GetIPString())); + break; + } + case DNSBLConfEntry::I_UNKNOWN: + { + break; + } + break; + } + + if (show) + { + ServerInstance->WriteOpers("*** Connecting user %s detected as being on a DNS blacklist (%s) with result %d", them->GetFullRealHost(), ConfEntry->name.c_str(), bitmask); + } + } + else + ConfEntry->stats_misses++; + } + else + ConfEntry->stats_misses++; + } + } + + virtual void OnError(ResolverError e, const std::string &errormessage) + { + } + + virtual ~DNSBLResolver() + { + } +}; + +class ModuleDNSBL : public Module +{ + private: + std::vector<DNSBLConfEntry *> DNSBLConfEntries; + + /* + * Convert a string to EnumBanaction + */ + DNSBLConfEntry::EnumBanaction str2banaction(const std::string &action) + { + if(action.compare("KILL")==0) + return DNSBLConfEntry::I_KILL; + if(action.compare("KLINE")==0) + return DNSBLConfEntry::I_KLINE; + if(action.compare("ZLINE")==0) + return DNSBLConfEntry::I_ZLINE; + if(action.compare("GLINE")==0) + return DNSBLConfEntry::I_GLINE; + + return DNSBLConfEntry::I_UNKNOWN; + } + public: + ModuleDNSBL(InspIRCd *Me) : Module(Me) + { + ReadConf(); + } + + virtual ~ModuleDNSBL() + { + ClearEntries(); + } + + virtual Version GetVersion() + { + return Version(2, 0, 0, 1, VF_VENDOR, API_VERSION); + } + + void Implements(char* List) + { + List[I_OnRehash] = List[I_OnUserRegister] = List[I_OnStats] = 1; + } + + /** Clear entries and free the mem it was using + */ + void ClearEntries() + { + std::vector<DNSBLConfEntry *>::iterator i; + for (std::vector<DNSBLConfEntry *>::iterator i = DNSBLConfEntries.begin(); i != DNSBLConfEntries.end(); i++) + delete *i; + DNSBLConfEntries.clear(); + } + + /** Fill our conf vector with data + */ + virtual void ReadConf() + { + ConfigReader *MyConf = new ConfigReader(ServerInstance); + ClearEntries(); + + for (int i=0; i< MyConf->Enumerate("dnsbl"); i++) + { + DNSBLConfEntry *e = new DNSBLConfEntry(); + + e->name = MyConf->ReadValue("dnsbl", "name", i); + e->reason = MyConf->ReadValue("dnsbl", "reason", i); + e->domain = MyConf->ReadValue("dnsbl", "domain", i); + e->banaction = str2banaction(MyConf->ReadValue("dnsbl", "action", i)); + e->duration = ServerInstance->Duration(MyConf->ReadValue("dnsbl", "duration", i)); + e->bitmask = MyConf->ReadInteger("dnsbl", "bitmask", i, false); + + /* yeah, logic here is a little messy */ + if (e->bitmask <= 0) + { + ServerInstance->WriteOpers("*** DNSBL(#%d): invalid bitmask",i); + } + else if (e->name.empty()) + { + ServerInstance->WriteOpers("*** DNSBL(#%d): Invalid name",i); + } + else if (e->domain.empty()) + { + ServerInstance->WriteOpers("*** DNSBL(#%d): Invalid domain",i); + } + else if (e->banaction == DNSBLConfEntry::I_UNKNOWN) + { + ServerInstance->WriteOpers("*** DNSBL(#%d): Invalid banaction", i); + } + else + { + if (e->reason.empty()) + { + ServerInstance->WriteOpers("*** DNSBL(#%d): empty reason, using defaults",i); + e->reason = "Your IP has been blacklisted."; + } + + /* add it, all is ok */ + DNSBLConfEntries.push_back(e); + continue; + } + + /* delete and drop it, error somewhere */ + delete e; + } + + delete MyConf; + } + + virtual void OnRehash(userrec* user, const std::string ¶meter) + { + ReadConf(); + } + + virtual int OnUserRegister(userrec* user) + { + /* only do lookups on local users */ + if (IS_LOCAL(user)) + { + /* following code taken from bopm, reverses an IP address. */ + struct in_addr in; + unsigned char a, b, c, d; + char reversedipbuf[128]; + std::string reversedip; + bool success = false; + + if (!inet_aton(user->GetIPString(), &in)) + { +#ifdef IPV6 + /* We could have an ipv6 address here */ + std::string x = user->GetIPString(); + /* Is it a 4in6 address? (Compensate for this kernel kludge that people love) */ + if (x.find("0::ffff:") == 0) + { + x.erase(x.begin(), x.begin() + 8); + if (inet_aton(x.c_str(), &in)) + success = true; + } +#endif + } + else + { + success = true; + } + + if (!success) + return 0; + + d = (unsigned char) (in.s_addr >> 24) & 0xFF; + c = (unsigned char) (in.s_addr >> 16) & 0xFF; + b = (unsigned char) (in.s_addr >> 8) & 0xFF; + a = (unsigned char) in.s_addr & 0xFF; + + snprintf(reversedipbuf, 128, "%d.%d.%d.%d", d, c, b, a); + reversedip = std::string(reversedipbuf); + + // For each DNSBL, we will run through this lookup + for (std::vector<DNSBLConfEntry *>::iterator i = DNSBLConfEntries.begin(); i != DNSBLConfEntries.end(); i++) + { + // Fill hostname with a dnsbl style host (d.c.b.a.domain.tld) + std::string hostname = reversedip + "." + (*i)->domain; + + /* now we'd need to fire off lookups for `hostname'. */ + bool cached; + DNSBLResolver *r = new DNSBLResolver(this, ServerInstance, hostname, user, user->GetFd(), *i, cached); + ServerInstance->AddResolver(r, cached); + } + } + + /* don't do anything with this hot potato */ + return 0; + } + + virtual int OnStats(char symbol, userrec* user, string_list &results) + { + if (symbol != 'd') + return 0; + + unsigned long total_hits = 0, total_misses = 0; + + for (std::vector<DNSBLConfEntry*>::iterator i = DNSBLConfEntries.begin(); i != DNSBLConfEntries.end(); i++) + { + total_hits += (*i)->stats_hits; + total_misses += (*i)->stats_misses; + + results.push_back(std::string(ServerInstance->Config->ServerName) + " 304 " + user->nick + " :DNSBLSTATS DNSbl \"" + (*i)->name + "\" had " + + ConvToStr((*i)->stats_hits) + " hits and " + ConvToStr((*i)->stats_misses) + " misses"); + } + + results.push_back(std::string(ServerInstance->Config->ServerName) + " 304 " + user->nick + " :DNSBLSTATS Total hits: " + ConvToStr(total_hits)); + results.push_back(std::string(ServerInstance->Config->ServerName) + " 304 " + user->nick + " :DNSBLSTATS Total misses: " + ConvToStr(total_misses)); + + return 0; + } +}; + +MODULE_INIT(ModuleDNSBL) diff --git a/src/modules/m_filter.cpp b/src/modules/m_filter.cpp index 9c16c8bf3..70de88e2c 100644 --- a/src/modules/m_filter.cpp +++ b/src/modules/m_filter.cpp @@ -1 +1,135 @@ -/* +------------------------------------+
* | 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 "users.h"
#include "channels.h"
#include "modules.h"
#include "m_filter.h"
/* $ModDesc: An advanced spam filtering module */
/* $ModDep: m_filter.h */
typedef std::map<std::string,FilterResult*> filter_t;
class ModuleFilter : public FilterBase
{
filter_t filters;
public:
ModuleFilter(InspIRCd* Me)
: FilterBase(Me, "m_filter.so")
{
OnRehash(NULL,"");
}
virtual ~ModuleFilter()
{
}
virtual FilterResult* FilterMatch(userrec* user, const std::string &text, int flags)
{
for (filter_t::iterator index = filters.begin(); index != filters.end(); index++)
{
/* Skip ones that dont apply to us */
if (!FilterBase::AppliesToMe(user, index->second, flags))
continue;
if (ServerInstance->MatchText(text,index->first))
{
FilterResult* fr = index->second;
if (index != filters.begin())
{
std::string pat = index->first;
filters.erase(index);
filters.insert(filters.begin(), std::make_pair(pat,fr));
}
return fr;
}
}
return NULL;
}
virtual bool DeleteFilter(const std::string &freeform)
{
if (filters.find(freeform) != filters.end())
{
delete (filters.find(freeform))->second;
filters.erase(filters.find(freeform));
return true;
}
return false;
}
virtual std::pair<bool, std::string> AddFilter(const std::string &freeform, const std::string &type, const std::string &reason, long duration, const std::string &flags)
{
if (filters.find(freeform) != filters.end())
{
return std::make_pair(false, "Filter already exists");
}
FilterResult* x = new FilterResult(freeform, reason, type, duration, flags);
filters[freeform] = x;
return std::make_pair(true, "");
}
virtual void SyncFilters(Module* proto, void* opaque)
{
for (filter_t::iterator n = filters.begin(); n != filters.end(); n++)
{
this->SendFilter(proto, opaque, n->second);
}
}
virtual void OnRehash(userrec* user, const std::string ¶meter)
{
ConfigReader* MyConf = new ConfigReader(ServerInstance);
for (int index = 0; index < MyConf->Enumerate("keyword"); index++)
{
this->DeleteFilter(MyConf->ReadValue("keyword","pattern",index));
std::string pattern = MyConf->ReadValue("keyword","pattern",index);
std::string reason = MyConf->ReadValue("keyword","reason",index);
std::string do_action = MyConf->ReadValue("keyword","action",index);
std::string flags = MyConf->ReadValue("keyword","flags",index);
long gline_time = ServerInstance->Duration(MyConf->ReadValue("keyword","duration",index));
if (do_action.empty())
do_action = "none";
if (flags.empty())
flags = "*";
FilterResult* x = new FilterResult(pattern, reason, do_action, gline_time, flags);
filters[pattern] = x;
}
DELETE(MyConf);
}
virtual int OnStats(char symbol, userrec* user, string_list &results)
{
if (symbol == 's')
{
std::string sn = ServerInstance->Config->ServerName;
for (filter_t::iterator n = filters.begin(); n != filters.end(); n++)
{
results.push_back(sn+" 223 "+user->nick+" :GLOB:"+n->second->freeform+" "+n->second->flags+" "+n->second->action+" "+ConvToStr(n->second->gline_time)+" :"+n->second->reason);
}
}
return 0;
}
};
MODULE_INIT(ModuleFilter)
\ No newline at end of file +/* +------------------------------------+ + * | 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 "users.h" +#include "channels.h" +#include "modules.h" +#include "m_filter.h" + +/* $ModDesc: An advanced spam filtering module */ +/* $ModDep: m_filter.h */ + +typedef std::map<std::string,FilterResult*> filter_t; + +class ModuleFilter : public FilterBase +{ + + filter_t filters; + + public: + ModuleFilter(InspIRCd* Me) + : FilterBase(Me, "m_filter.so") + { + OnRehash(NULL,""); + } + + virtual ~ModuleFilter() + { + } + + virtual FilterResult* FilterMatch(userrec* user, const std::string &text, int flags) + { + for (filter_t::iterator index = filters.begin(); index != filters.end(); index++) + { + + /* Skip ones that dont apply to us */ + if (!FilterBase::AppliesToMe(user, index->second, flags)) + continue; + + if (ServerInstance->MatchText(text,index->first)) + { + FilterResult* fr = index->second; + if (index != filters.begin()) + { + std::string pat = index->first; + filters.erase(index); + filters.insert(filters.begin(), std::make_pair(pat,fr)); + } + return fr; + } + } + return NULL; + } + + virtual bool DeleteFilter(const std::string &freeform) + { + if (filters.find(freeform) != filters.end()) + { + delete (filters.find(freeform))->second; + filters.erase(filters.find(freeform)); + return true; + } + return false; + } + + virtual std::pair<bool, std::string> AddFilter(const std::string &freeform, const std::string &type, const std::string &reason, long duration, const std::string &flags) + { + if (filters.find(freeform) != filters.end()) + { + return std::make_pair(false, "Filter already exists"); + } + + FilterResult* x = new FilterResult(freeform, reason, type, duration, flags); + filters[freeform] = x; + + return std::make_pair(true, ""); + } + + virtual void SyncFilters(Module* proto, void* opaque) + { + for (filter_t::iterator n = filters.begin(); n != filters.end(); n++) + { + this->SendFilter(proto, opaque, n->second); + } + } + + virtual void OnRehash(userrec* user, const std::string ¶meter) + { + ConfigReader* MyConf = new ConfigReader(ServerInstance); + + for (int index = 0; index < MyConf->Enumerate("keyword"); index++) + { + this->DeleteFilter(MyConf->ReadValue("keyword","pattern",index)); + + std::string pattern = MyConf->ReadValue("keyword","pattern",index); + std::string reason = MyConf->ReadValue("keyword","reason",index); + std::string do_action = MyConf->ReadValue("keyword","action",index); + std::string flags = MyConf->ReadValue("keyword","flags",index); + long gline_time = ServerInstance->Duration(MyConf->ReadValue("keyword","duration",index)); + if (do_action.empty()) + do_action = "none"; + if (flags.empty()) + flags = "*"; + FilterResult* x = new FilterResult(pattern, reason, do_action, gline_time, flags); + filters[pattern] = x; + } + DELETE(MyConf); + } + + virtual int OnStats(char symbol, userrec* user, string_list &results) + { + if (symbol == 's') + { + std::string sn = ServerInstance->Config->ServerName; + for (filter_t::iterator n = filters.begin(); n != filters.end(); n++) + { + results.push_back(sn+" 223 "+user->nick+" :GLOB:"+n->second->freeform+" "+n->second->flags+" "+n->second->action+" "+ConvToStr(n->second->gline_time)+" :"+n->second->reason); + } + } + return 0; + } +}; + + +MODULE_INIT(ModuleFilter) diff --git a/src/modules/m_filter.h b/src/modules/m_filter.h index ddf448e2e..f2986804c 100644 --- a/src/modules/m_filter.h +++ b/src/modules/m_filter.h @@ -1 +1,453 @@ -/* +------------------------------------+
* | 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 "xline.h"
enum FilterFlags
{
FLAG_NOOPERS = 1,
FLAG_PART = 2,
FLAG_QUIT = 4,
FLAG_PRIVMSG = 8,
FLAG_NOTICE = 16
};
class FilterResult : public classbase
{
public:
std::string freeform;
std::string reason;
std::string action;
long gline_time;
std::string flags;
bool flag_no_opers;
bool flag_part_message;
bool flag_quit_message;
bool flag_privmsg;
bool flag_notice;
FilterResult(const std::string free, const std::string &rea, const std::string &act, long gt, const std::string &fla) : freeform(free), reason(rea),
action(act), gline_time(gt), flags(fla)
{
this->FillFlags(flags);
}
int FillFlags(const std::string &fl)
{
flags = fl;
flag_no_opers = flag_part_message = flag_quit_message = flag_privmsg = flag_notice = false;
size_t x = 0;
for (std::string::const_iterator n = flags.begin(); n != flags.end(); ++n, ++x)
{
switch (*n)
{
case 'o':
flag_no_opers = true;
break;
case 'P':
flag_part_message = true;
break;
case 'q':
flag_quit_message = true;
break;
case 'p':
flag_privmsg = true;
break;
case 'n':
flag_notice = true;
break;
case '*':
flag_no_opers = flag_part_message = flag_quit_message =
flag_privmsg = flag_notice = true;
break;
default:
return x;
break;
}
}
return 0;
}
FilterResult()
{
}
virtual ~FilterResult()
{
}
};
class cmd_filter;
class FilterBase : public Module
{
cmd_filter* filtcommand;
int flags;
public:
FilterBase(InspIRCd* Me, const std::string &source);
virtual ~FilterBase();
virtual void Implements(char* List);
virtual int OnUserPreMessage(userrec* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list);
virtual FilterResult* FilterMatch(userrec* user, const std::string &text, int flags) = 0;
virtual bool DeleteFilter(const std::string &freeform) = 0;
virtual void SyncFilters(Module* proto, void* opaque) = 0;
virtual void SendFilter(Module* proto, void* opaque, FilterResult* iter);
virtual std::pair<bool, std::string> AddFilter(const std::string &freeform, const std::string &type, const std::string &reason, long duration, const std::string &flags) = 0;
virtual int OnUserPreNotice(userrec* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list);
virtual void OnRehash(userrec* user, const std::string ¶meter);
virtual Version GetVersion();
std::string EncodeFilter(FilterResult* filter);
FilterResult DecodeFilter(const std::string &data);
virtual void OnSyncOtherMetaData(Module* proto, void* opaque, bool displayable = false);
virtual void OnDecodeMetaData(int target_type, void* target, const std::string &extname, const std::string &extdata);
virtual int OnStats(char symbol, userrec* user, string_list &results) = 0;
virtual int OnPreCommand(const std::string &command, const char** parameters, int pcnt, userrec *user, bool validated, const std::string &original_line);
bool AppliesToMe(userrec* user, FilterResult* filter, int flags);
};
class cmd_filter : public command_t
{
FilterBase* Base;
public:
cmd_filter(FilterBase* f, InspIRCd* Me, const std::string &source) : command_t(Me, "FILTER", 'o', 1), Base(f)
{
this->source = source;
this->syntax = "<filter-definition> <type> <flags> [<gline-duration>] :<reason>";
}
CmdResult Handle(const char** parameters, int pcnt, userrec *user)
{
if (pcnt == 1)
{
/* Deleting a filter */
if (Base->DeleteFilter(parameters[0]))
{
user->WriteServ("NOTICE %s :*** Deleted filter '%s'", user->nick, parameters[0]);
return CMD_SUCCESS;
}
else
{
user->WriteServ("NOTICE %s :*** Filter '%s' not found on list.", user->nick, parameters[0]);
return CMD_FAILURE;
}
}
else
{
/* Adding a filter */
if (pcnt >= 4)
{
std::string freeform = parameters[0];
std::string type = parameters[1];
std::string flags = parameters[2];
std::string reason;
long duration = 0;
if ((type != "gline") && (type != "none") && (type != "block") && (type != "kill") && (type != "silent"))
{
user->WriteServ("NOTICE %s :*** Invalid filter type '%s'. Supported types are 'gline', 'none', 'block', 'silent' and 'kill'.", user->nick, freeform.c_str());
return CMD_FAILURE;
}
if (type == "gline")
{
if (pcnt >= 5)
{
duration = ServerInstance->Duration(parameters[3]);
reason = parameters[4];
}
else
{
this->TooFewParams(user, " When setting a gline type filter, a gline duration must be specified as the third parameter.");
return CMD_FAILURE;
}
}
else
{
reason = parameters[3];
}
std::pair<bool, std::string> result = Base->AddFilter(freeform, type, reason, duration, flags);
if (result.first)
{
user->WriteServ("NOTICE %s :*** Added filter '%s', type '%s'%s%s, flags '%s', reason: '%s'", user->nick, freeform.c_str(),
type.c_str(), (duration ? " duration: " : ""), (duration ? parameters[3] : ""),
flags.c_str(), reason.c_str());
return CMD_SUCCESS;
}
else
{
user->WriteServ("NOTICE %s :*** Filter '%s' could not be added: %s", user->nick, freeform.c_str(), result.second.c_str());
return CMD_FAILURE;
}
}
else
{
this->TooFewParams(user, ".");
return CMD_FAILURE;
}
}
}
void TooFewParams(userrec* user, const std::string &extra_text)
{
user->WriteServ("NOTICE %s :*** Not enough parameters%s", user->nick, extra_text.c_str());
}
};
bool FilterBase::AppliesToMe(userrec* user, FilterResult* filter, int flags)
{
if ((flags & FLAG_NOOPERS) && (filter->flag_no_opers) && IS_OPER(user))
return false;
if ((flags & FLAG_PRIVMSG) && (!filter->flag_privmsg))
return false;
if ((flags & FLAG_NOTICE) && (!filter->flag_notice))
return false;
if ((flags & FLAG_QUIT) && (!filter->flag_quit_message))
return false;
if ((flags & FLAG_PART) && (!filter->flag_part_message))
return false;
return true;
}
FilterBase::FilterBase(InspIRCd* Me, const std::string &source) : Module(Me)
{
filtcommand = new cmd_filter(this, Me, source);
ServerInstance->AddCommand(filtcommand);
}
FilterBase::~FilterBase()
{
}
void FilterBase::Implements(char* List)
{
List[I_OnPreCommand] = List[I_OnStats] = List[I_OnSyncOtherMetaData] = List[I_OnDecodeMetaData] = List[I_OnUserPreMessage] = List[I_OnUserPreNotice] = List[I_OnRehash] = 1;
}
int FilterBase::OnUserPreMessage(userrec* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list)
{
flags = FLAG_PRIVMSG;
return OnUserPreNotice(user,dest,target_type,text,status,exempt_list);
}
int FilterBase::OnUserPreNotice(userrec* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list)
{
if (!flags)
flags = FLAG_NOTICE;
/* Leave ulines alone */
if ((ServerInstance->ULine(user->server)) || (!IS_LOCAL(user)))
return 0;
FilterResult* f = this->FilterMatch(user, text, flags);
if (f)
{
std::string target = "";
if (target_type == TYPE_USER)
{
userrec* t = (userrec*)dest;
target = std::string(t->nick);
}
else if (target_type == TYPE_CHANNEL)
{
chanrec* t = (chanrec*)dest;
target = std::string(t->name);
}
if (f->action == "block")
{
ServerInstance->WriteOpers(std::string("FILTER: ")+user->nick+" had their message filtered, target was "+target+": "+f->reason);
user->WriteServ("NOTICE "+std::string(user->nick)+" :Your message has been filtered and opers notified: "+f->reason);
}
if (f->action == "silent")
{
user->WriteServ("NOTICE "+std::string(user->nick)+" :Your message has been filtered: "+f->reason);
}
if (f->action == "kill")
{
userrec::QuitUser(ServerInstance,user,"Filtered: "+f->reason);
}
if (f->action == "gline")
{
if (ServerInstance->XLines->add_gline(f->gline_time, ServerInstance->Config->ServerName, f->reason.c_str(), user->MakeHostIP()))
{
ServerInstance->XLines->apply_lines(APPLY_GLINES);
FOREACH_MOD(I_OnAddGLine,OnAddGLine(f->gline_time, NULL, f->reason, user->MakeHostIP()));
}
}
ServerInstance->Log(DEFAULT,"FILTER: "+std::string(user->nick)+std::string(" had their message filtered, target was ")+target+": "+f->reason+" Action: "+f->action);
return 1;
}
return 0;
}
int FilterBase::OnPreCommand(const std::string &command, const char** parameters, int pcnt, userrec *user, bool validated, const std::string &original_line)
{
flags = 0;
if ((validated == 1) && (IS_LOCAL(user)))
{
std::string checkline;
int replacepoint = 0;
bool parting = false;
if (command == "QUIT")
{
/* QUIT with no reason: nothing to do */
if (pcnt < 1)
return 0;
checkline = parameters[0];
replacepoint = 0;
parting = false;
flags = FLAG_QUIT;
}
else if (command == "PART")
{
/* PART with no reason: nothing to do */
if (pcnt < 2)
return 0;
checkline = parameters[1];
replacepoint = 1;
parting = true;
flags = FLAG_PART;
}
else
/* We're only messing with PART and QUIT */
return 0;
FilterResult* f = NULL;
if (flags)
f = this->FilterMatch(user, checkline, flags);
if (!f)
/* PART or QUIT reason doesnt match a filter */
return 0;
/* We cant block a part or quit, so instead we change the reason to 'Reason filtered' */
command_t* c = ServerInstance->Parser->GetHandler(command);
if (c)
{
const char* params[127];
for (int item = 0; item < pcnt; item++)
params[item] = parameters[item];
params[replacepoint] = "Reason filtered";
/* We're blocking, OR theyre quitting and its a KILL action
* (we cant kill someone whos already quitting, so filter them anyway)
*/
if ((f->action == "block") || (((!parting) && (f->action == "kill"))) || (f->action == "silent"))
{
c->Handle(params, pcnt, user);
return 1;
}
else
{
/* Are they parting, if so, kill is applicable */
if ((parting) && (f->action == "kill"))
{
user->SetWriteError("Filtered: "+f->reason);
/* This WriteServ causes the write error to be applied.
* Its not safe to kill here with QuitUser in a PreCommand handler,
* so we do it this way, which is safe just about anywhere.
*/
user->WriteServ("NOTICE %s :*** Your PART message was filtered: %s", user->nick, f->reason.c_str());
}
if (f->action == "gline")
{
/* Note: We gline *@IP so that if their host doesnt resolve the gline still applies. */
std::string wild = "*@";
wild.append(user->GetIPString());
if (ServerInstance->XLines->add_gline(f->gline_time, ServerInstance->Config->ServerName, f->reason.c_str(), wild.c_str()))
{
ServerInstance->XLines->apply_lines(APPLY_GLINES);
FOREACH_MOD(I_OnAddGLine,OnAddGLine(f->gline_time, NULL, f->reason, user->MakeHostIP()));
}
}
return 1;
}
}
return 0;
}
return 0;
}
void FilterBase::OnRehash(userrec* user, const std::string ¶meter)
{
}
Version FilterBase::GetVersion()
{
return Version(1,1,0,2,VF_VENDOR|VF_COMMON,API_VERSION);
}
std::string FilterBase::EncodeFilter(FilterResult* filter)
{
std::ostringstream stream;
std::string x = filter->freeform;
/* Hax to allow spaces in the freeform without changing the design of the irc protocol */
for (std::string::iterator n = x.begin(); n != x.end(); n++)
if (*n == ' ')
*n = '\7';
stream << x << " " << filter->action << " " << (filter->flags.empty() ? "-" : filter->flags) << " " << filter->gline_time << " :" << filter->reason;
return stream.str();
}
FilterResult FilterBase::DecodeFilter(const std::string &data)
{
FilterResult res;
irc::tokenstream tokens(data);
tokens.GetToken(res.freeform);
tokens.GetToken(res.action);
tokens.GetToken(res.flags);
if (res.flags == "-")
res.flags = "";
res.FillFlags(res.flags);
tokens.GetToken(res.gline_time);
tokens.GetToken(res.reason);
/* Hax to allow spaces in the freeform without changing the design of the irc protocol */
for (std::string::iterator n = res.freeform.begin(); n != res.freeform.end(); n++)
if (*n == '\7')
*n = ' ';
return res;
}
void FilterBase::OnSyncOtherMetaData(Module* proto, void* opaque, bool displayable)
{
this->SyncFilters(proto, opaque);
}
void FilterBase::SendFilter(Module* proto, void* opaque, FilterResult* iter)
{
proto->ProtoSendMetaData(opaque, TYPE_OTHER, NULL, "filter", EncodeFilter(iter));
}
void FilterBase::OnDecodeMetaData(int target_type, void* target, const std::string &extname, const std::string &extdata)
{
if ((target_type == TYPE_OTHER) && (extname == "filter"))
{
FilterResult data = DecodeFilter(extdata);
this->AddFilter(data.freeform, data.action, data.reason, data.gline_time, data.flags);
}
}
\ No newline at end of file +/* +------------------------------------+ + * | 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 "xline.h" + +enum FilterFlags +{ + FLAG_NOOPERS = 1, + FLAG_PART = 2, + FLAG_QUIT = 4, + FLAG_PRIVMSG = 8, + FLAG_NOTICE = 16 +}; + +class FilterResult : public classbase +{ + public: + std::string freeform; + std::string reason; + std::string action; + long gline_time; + std::string flags; + + bool flag_no_opers; + bool flag_part_message; + bool flag_quit_message; + bool flag_privmsg; + bool flag_notice; + + FilterResult(const std::string free, const std::string &rea, const std::string &act, long gt, const std::string &fla) : freeform(free), reason(rea), + action(act), gline_time(gt), flags(fla) + { + this->FillFlags(flags); + } + + int FillFlags(const std::string &fl) + { + flags = fl; + flag_no_opers = flag_part_message = flag_quit_message = flag_privmsg = flag_notice = false; + size_t x = 0; + + for (std::string::const_iterator n = flags.begin(); n != flags.end(); ++n, ++x) + { + switch (*n) + { + case 'o': + flag_no_opers = true; + break; + case 'P': + flag_part_message = true; + break; + case 'q': + flag_quit_message = true; + break; + case 'p': + flag_privmsg = true; + break; + case 'n': + flag_notice = true; + break; + case '*': + flag_no_opers = flag_part_message = flag_quit_message = + flag_privmsg = flag_notice = true; + break; + default: + return x; + break; + } + } + return 0; + } + + FilterResult() + { + } + + virtual ~FilterResult() + { + } +}; + +class cmd_filter; + +class FilterBase : public Module +{ + cmd_filter* filtcommand; + int flags; + public: + FilterBase(InspIRCd* Me, const std::string &source); + virtual ~FilterBase(); + virtual void Implements(char* List); + virtual int OnUserPreMessage(userrec* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list); + virtual FilterResult* FilterMatch(userrec* user, const std::string &text, int flags) = 0; + virtual bool DeleteFilter(const std::string &freeform) = 0; + virtual void SyncFilters(Module* proto, void* opaque) = 0; + virtual void SendFilter(Module* proto, void* opaque, FilterResult* iter); + virtual std::pair<bool, std::string> AddFilter(const std::string &freeform, const std::string &type, const std::string &reason, long duration, const std::string &flags) = 0; + virtual int OnUserPreNotice(userrec* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list); + virtual void OnRehash(userrec* user, const std::string ¶meter); + virtual Version GetVersion(); + std::string EncodeFilter(FilterResult* filter); + FilterResult DecodeFilter(const std::string &data); + virtual void OnSyncOtherMetaData(Module* proto, void* opaque, bool displayable = false); + virtual void OnDecodeMetaData(int target_type, void* target, const std::string &extname, const std::string &extdata); + virtual int OnStats(char symbol, userrec* user, string_list &results) = 0; + virtual int OnPreCommand(const std::string &command, const char** parameters, int pcnt, userrec *user, bool validated, const std::string &original_line); + bool AppliesToMe(userrec* user, FilterResult* filter, int flags); +}; + +class cmd_filter : public command_t +{ + FilterBase* Base; + public: + cmd_filter(FilterBase* f, InspIRCd* Me, const std::string &source) : command_t(Me, "FILTER", 'o', 1), Base(f) + { + this->source = source; + this->syntax = "<filter-definition> <type> <flags> [<gline-duration>] :<reason>"; + } + + CmdResult Handle(const char** parameters, int pcnt, userrec *user) + { + if (pcnt == 1) + { + /* Deleting a filter */ + if (Base->DeleteFilter(parameters[0])) + { + user->WriteServ("NOTICE %s :*** Deleted filter '%s'", user->nick, parameters[0]); + return CMD_SUCCESS; + } + else + { + user->WriteServ("NOTICE %s :*** Filter '%s' not found on list.", user->nick, parameters[0]); + return CMD_FAILURE; + } + } + else + { + /* Adding a filter */ + if (pcnt >= 4) + { + std::string freeform = parameters[0]; + std::string type = parameters[1]; + std::string flags = parameters[2]; + std::string reason; + long duration = 0; + + + if ((type != "gline") && (type != "none") && (type != "block") && (type != "kill") && (type != "silent")) + { + user->WriteServ("NOTICE %s :*** Invalid filter type '%s'. Supported types are 'gline', 'none', 'block', 'silent' and 'kill'.", user->nick, freeform.c_str()); + return CMD_FAILURE; + } + + if (type == "gline") + { + if (pcnt >= 5) + { + duration = ServerInstance->Duration(parameters[3]); + reason = parameters[4]; + } + else + { + this->TooFewParams(user, " When setting a gline type filter, a gline duration must be specified as the third parameter."); + return CMD_FAILURE; + } + } + else + { + reason = parameters[3]; + } + std::pair<bool, std::string> result = Base->AddFilter(freeform, type, reason, duration, flags); + if (result.first) + { + user->WriteServ("NOTICE %s :*** Added filter '%s', type '%s'%s%s, flags '%s', reason: '%s'", user->nick, freeform.c_str(), + type.c_str(), (duration ? " duration: " : ""), (duration ? parameters[3] : ""), + flags.c_str(), reason.c_str()); + return CMD_SUCCESS; + } + else + { + user->WriteServ("NOTICE %s :*** Filter '%s' could not be added: %s", user->nick, freeform.c_str(), result.second.c_str()); + return CMD_FAILURE; + } + } + else + { + this->TooFewParams(user, "."); + return CMD_FAILURE; + } + + } + } + + void TooFewParams(userrec* user, const std::string &extra_text) + { + user->WriteServ("NOTICE %s :*** Not enough parameters%s", user->nick, extra_text.c_str()); + } +}; + +bool FilterBase::AppliesToMe(userrec* user, FilterResult* filter, int flags) +{ + if ((flags & FLAG_NOOPERS) && (filter->flag_no_opers) && IS_OPER(user)) + return false; + if ((flags & FLAG_PRIVMSG) && (!filter->flag_privmsg)) + return false; + if ((flags & FLAG_NOTICE) && (!filter->flag_notice)) + return false; + if ((flags & FLAG_QUIT) && (!filter->flag_quit_message)) + return false; + if ((flags & FLAG_PART) && (!filter->flag_part_message)) + return false; + return true; +} + +FilterBase::FilterBase(InspIRCd* Me, const std::string &source) : Module(Me) +{ + filtcommand = new cmd_filter(this, Me, source); + ServerInstance->AddCommand(filtcommand); +} + +FilterBase::~FilterBase() +{ +} + +void FilterBase::Implements(char* List) +{ + List[I_OnPreCommand] = List[I_OnStats] = List[I_OnSyncOtherMetaData] = List[I_OnDecodeMetaData] = List[I_OnUserPreMessage] = List[I_OnUserPreNotice] = List[I_OnRehash] = 1; +} + +int FilterBase::OnUserPreMessage(userrec* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list) +{ + flags = FLAG_PRIVMSG; + return OnUserPreNotice(user,dest,target_type,text,status,exempt_list); +} + +int FilterBase::OnUserPreNotice(userrec* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list) +{ + if (!flags) + flags = FLAG_NOTICE; + + /* Leave ulines alone */ + if ((ServerInstance->ULine(user->server)) || (!IS_LOCAL(user))) + return 0; + + FilterResult* f = this->FilterMatch(user, text, flags); + if (f) + { + std::string target = ""; + if (target_type == TYPE_USER) + { + userrec* t = (userrec*)dest; + target = std::string(t->nick); + } + else if (target_type == TYPE_CHANNEL) + { + chanrec* t = (chanrec*)dest; + target = std::string(t->name); + } + if (f->action == "block") + { + ServerInstance->WriteOpers(std::string("FILTER: ")+user->nick+" had their message filtered, target was "+target+": "+f->reason); + user->WriteServ("NOTICE "+std::string(user->nick)+" :Your message has been filtered and opers notified: "+f->reason); + } + if (f->action == "silent") + { + user->WriteServ("NOTICE "+std::string(user->nick)+" :Your message has been filtered: "+f->reason); + } + if (f->action == "kill") + { + userrec::QuitUser(ServerInstance,user,"Filtered: "+f->reason); + } + if (f->action == "gline") + { + if (ServerInstance->XLines->add_gline(f->gline_time, ServerInstance->Config->ServerName, f->reason.c_str(), user->MakeHostIP())) + { + ServerInstance->XLines->apply_lines(APPLY_GLINES); + FOREACH_MOD(I_OnAddGLine,OnAddGLine(f->gline_time, NULL, f->reason, user->MakeHostIP())); + } + } + + ServerInstance->Log(DEFAULT,"FILTER: "+std::string(user->nick)+std::string(" had their message filtered, target was ")+target+": "+f->reason+" Action: "+f->action); + return 1; + } + return 0; +} + +int FilterBase::OnPreCommand(const std::string &command, const char** parameters, int pcnt, userrec *user, bool validated, const std::string &original_line) +{ + flags = 0; + if ((validated == 1) && (IS_LOCAL(user))) + { + std::string checkline; + int replacepoint = 0; + bool parting = false; + + if (command == "QUIT") + { + /* QUIT with no reason: nothing to do */ + if (pcnt < 1) + return 0; + + checkline = parameters[0]; + replacepoint = 0; + parting = false; + flags = FLAG_QUIT; + } + else if (command == "PART") + { + /* PART with no reason: nothing to do */ + if (pcnt < 2) + return 0; + + checkline = parameters[1]; + replacepoint = 1; + parting = true; + flags = FLAG_PART; + } + else + /* We're only messing with PART and QUIT */ + return 0; + + FilterResult* f = NULL; + + if (flags) + f = this->FilterMatch(user, checkline, flags); + + if (!f) + /* PART or QUIT reason doesnt match a filter */ + return 0; + + /* We cant block a part or quit, so instead we change the reason to 'Reason filtered' */ + command_t* c = ServerInstance->Parser->GetHandler(command); + if (c) + { + const char* params[127]; + for (int item = 0; item < pcnt; item++) + params[item] = parameters[item]; + params[replacepoint] = "Reason filtered"; + + /* We're blocking, OR theyre quitting and its a KILL action + * (we cant kill someone whos already quitting, so filter them anyway) + */ + if ((f->action == "block") || (((!parting) && (f->action == "kill"))) || (f->action == "silent")) + { + c->Handle(params, pcnt, user); + return 1; + } + else + { + /* Are they parting, if so, kill is applicable */ + if ((parting) && (f->action == "kill")) + { + user->SetWriteError("Filtered: "+f->reason); + /* This WriteServ causes the write error to be applied. + * Its not safe to kill here with QuitUser in a PreCommand handler, + * so we do it this way, which is safe just about anywhere. + */ + user->WriteServ("NOTICE %s :*** Your PART message was filtered: %s", user->nick, f->reason.c_str()); + } + if (f->action == "gline") + { + /* Note: We gline *@IP so that if their host doesnt resolve the gline still applies. */ + std::string wild = "*@"; + wild.append(user->GetIPString()); + + if (ServerInstance->XLines->add_gline(f->gline_time, ServerInstance->Config->ServerName, f->reason.c_str(), wild.c_str())) + { + ServerInstance->XLines->apply_lines(APPLY_GLINES); + FOREACH_MOD(I_OnAddGLine,OnAddGLine(f->gline_time, NULL, f->reason, user->MakeHostIP())); + } + } + return 1; + } + } + return 0; + } + return 0; +} + +void FilterBase::OnRehash(userrec* user, const std::string ¶meter) +{ +} + +Version FilterBase::GetVersion() +{ + return Version(1,1,0,2,VF_VENDOR|VF_COMMON,API_VERSION); +} + + +std::string FilterBase::EncodeFilter(FilterResult* filter) +{ + std::ostringstream stream; + std::string x = filter->freeform; + + /* Hax to allow spaces in the freeform without changing the design of the irc protocol */ + for (std::string::iterator n = x.begin(); n != x.end(); n++) + if (*n == ' ') + *n = '\7'; + + stream << x << " " << filter->action << " " << (filter->flags.empty() ? "-" : filter->flags) << " " << filter->gline_time << " :" << filter->reason; + return stream.str(); +} + +FilterResult FilterBase::DecodeFilter(const std::string &data) +{ + FilterResult res; + irc::tokenstream tokens(data); + tokens.GetToken(res.freeform); + tokens.GetToken(res.action); + tokens.GetToken(res.flags); + if (res.flags == "-") + res.flags = ""; + res.FillFlags(res.flags); + tokens.GetToken(res.gline_time); + tokens.GetToken(res.reason); + + /* Hax to allow spaces in the freeform without changing the design of the irc protocol */ + for (std::string::iterator n = res.freeform.begin(); n != res.freeform.end(); n++) + if (*n == '\7') + *n = ' '; + + return res; +} + +void FilterBase::OnSyncOtherMetaData(Module* proto, void* opaque, bool displayable) +{ + this->SyncFilters(proto, opaque); +} + +void FilterBase::SendFilter(Module* proto, void* opaque, FilterResult* iter) +{ + proto->ProtoSendMetaData(opaque, TYPE_OTHER, NULL, "filter", EncodeFilter(iter)); +} + +void FilterBase::OnDecodeMetaData(int target_type, void* target, const std::string &extname, const std::string &extdata) +{ + if ((target_type == TYPE_OTHER) && (extname == "filter")) + { + FilterResult data = DecodeFilter(extdata); + this->AddFilter(data.freeform, data.action, data.reason, data.gline_time, data.flags); + } +} + diff --git a/src/modules/m_foobar.cpp b/src/modules/m_foobar.cpp index 857f4d16d..7de305923 100644 --- a/src/modules/m_foobar.cpp +++ b/src/modules/m_foobar.cpp @@ -1 +1,98 @@ -/* +------------------------------------+
* | 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 "users.h"
#include "channels.h"
#include "modules.h"
/* $ModDesc: A dummy module for testing */
// Class ModuleFoobar inherits from Module
// It just outputs simple debug strings to show its methods are working.
class ModuleFoobar : public Module
{
private:
// It is recommended that your class makes use of one or more Server
// objects. A server object is a class which contains methods which
// encapsulate the exports from the core of the ircd.
// such methods include Debug, SendChannel, etc.
public:
ModuleFoobar(InspIRCd* Me)
: Module(Me)
{
// The constructor just makes a copy of the server class
}
virtual ~ModuleFoobar()
{
}
virtual Version GetVersion()
{
// this method instantiates a class of type Version, and returns
// the modules version information using it.
return Version(1,1,0,1,VF_VENDOR,API_VERSION);
}
void Implements(char* List)
{
List[I_OnUserConnect] = List[I_OnUserQuit] = List[I_OnUserJoin] = List[I_OnUserPart] = 1;
}
virtual void OnUserConnect(userrec* user)
{
// method called when a user connects
std::string b = user->nick;
ServerInstance->Log(DEBUG,"Foobar: User connecting: "+b);
}
virtual void OnUserQuit(userrec* user, const std::string &reason, const std::string &oper_message)
{
// method called when a user disconnects
std::string b = user->nick;
ServerInstance->Log(DEBUG,"Foobar: User quitting: "+b);
}
virtual void OnUserJoin(userrec* user, chanrec* channel, bool &silent)
{
// method called when a user joins a channel
std::string c = channel->name;
std::string b = user->nick;
ServerInstance->Log(DEBUG,"Foobar: User "+b+" joined "+c);
}
virtual void OnUserPart(userrec* user, chanrec* channel, const std::string &partreason, bool &silent)
{
// method called when a user parts a channel
std::string c = channel->name;
std::string b = user->nick;
ServerInstance->Log(DEBUG,"Foobar: User "+b+" parted "+c);
}
};
MODULE_INIT(ModuleFoobar)
\ No newline at end of file +/* +------------------------------------+ + * | 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 "users.h" +#include "channels.h" +#include "modules.h" + +/* $ModDesc: A dummy module for testing */ + +// Class ModuleFoobar inherits from Module +// It just outputs simple debug strings to show its methods are working. + +class ModuleFoobar : public Module +{ + private: + + // It is recommended that your class makes use of one or more Server + // objects. A server object is a class which contains methods which + // encapsulate the exports from the core of the ircd. + // such methods include Debug, SendChannel, etc. + + + public: + ModuleFoobar(InspIRCd* Me) + : Module(Me) + { + // The constructor just makes a copy of the server class + + + } + + virtual ~ModuleFoobar() + { + } + + virtual Version GetVersion() + { + // this method instantiates a class of type Version, and returns + // the modules version information using it. + + return Version(1,1,0,1,VF_VENDOR,API_VERSION); + } + + void Implements(char* List) + { + List[I_OnUserConnect] = List[I_OnUserQuit] = List[I_OnUserJoin] = List[I_OnUserPart] = 1; + } + + virtual void OnUserConnect(userrec* user) + { + // method called when a user connects + + std::string b = user->nick; + ServerInstance->Log(DEBUG,"Foobar: User connecting: "+b); + } + + virtual void OnUserQuit(userrec* user, const std::string &reason, const std::string &oper_message) + { + // method called when a user disconnects + + std::string b = user->nick; + ServerInstance->Log(DEBUG,"Foobar: User quitting: "+b); + } + + virtual void OnUserJoin(userrec* user, chanrec* channel, bool &silent) + { + // method called when a user joins a channel + + std::string c = channel->name; + std::string b = user->nick; + ServerInstance->Log(DEBUG,"Foobar: User "+b+" joined "+c); + } + + virtual void OnUserPart(userrec* user, chanrec* channel, const std::string &partreason, bool &silent) + { + // method called when a user parts a channel + + std::string c = channel->name; + std::string b = user->nick; + ServerInstance->Log(DEBUG,"Foobar: User "+b+" parted "+c); + } + +}; + + +MODULE_INIT(ModuleFoobar) + diff --git a/src/modules/m_globalload.cpp b/src/modules/m_globalload.cpp index 4f3438e05..ae87451ba 100644 --- a/src/modules/m_globalload.cpp +++ b/src/modules/m_globalload.cpp @@ -1 +1,141 @@ -/* +------------------------------------+
* | 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.
*
* ---------------------------------------------------
*/
/* $ModDesc: Allows global loading of a module. */
#include "inspircd.h"
#include "users.h"
#include "channels.h"
#include "modules.h"
/** Handle /GLOADMODULE
*/
class cmd_gloadmodule : public command_t
{
public:
cmd_gloadmodule (InspIRCd* Instance) : command_t(Instance,"GLOADMODULE", 'o', 1)
{
this->source = "m_globalload.so";
syntax = "<modulename>";
}
CmdResult Handle (const char** parameters, int pcnt, userrec *user)
{
if (ServerInstance->LoadModule(parameters[0]))
{
ServerInstance->WriteOpers("*** NEW MODULE '%s' GLOBALLY LOADED BY '%s'",parameters[0],user->nick);
user->WriteServ("975 %s %s :Module successfully loaded.",user->nick, parameters[0]);
/* route it! */
return CMD_SUCCESS;
}
else
{
user->WriteServ("974 %s %s :Failed to load module: %s",user->nick, parameters[0],ServerInstance->ModuleError());
/* XXX - returning CMD_FAILURE here could potentially mean half the net loads it, half doesn't. pass it on anyway? -- w00t
*
* Returning CMD_SUCCESS would have the same effect, just with less servers. Someone should update this module to properly
* pass the success/failure for each server to the caller (or to all opers) -Special */
return CMD_FAILURE;
}
}
};
/** Handle /GUNLOADMODULE
*/
class cmd_gunloadmodule : public command_t
{
public:
cmd_gunloadmodule (InspIRCd* Instance) : command_t(Instance,"GUNLOADMODULE", 'o', 1)
{
this->source = "m_globalload.so";
syntax = "<modulename>";
}
CmdResult Handle (const char** parameters, int pcnt, userrec *user)
{
if (ServerInstance->UnloadModule(parameters[0]))
{
ServerInstance->WriteOpers("*** MODULE '%s' GLOBALLY UNLOADED BY '%s'",parameters[0],user->nick);
user->WriteServ("973 %s %s :Module successfully unloaded.",user->nick, parameters[0]);
}
else
{
/* Return CMD_SUCCESS so the module will be unloaded on any servers it is loaded on - this is a seperate case entirely from loading -Special */
user->WriteServ("972 %s %s :Failed to unload module: %s",user->nick, parameters[0],ServerInstance->ModuleError());
}
return CMD_SUCCESS;
}
};
/** Handle /GRELOADMODULE
*/
class cmd_greloadmodule : public command_t
{
public:
cmd_greloadmodule (InspIRCd* Instance) : command_t(Instance, "GRELOADMODULE", 'o', 1)
{
this->source = "m_globalload.so";
syntax = "<modulename>";
}
CmdResult Handle(const char** parameters, int pcnt, userrec *user)
{
if (!ServerInstance->UnloadModule(parameters[0]))
{
user->WriteServ("972 %s %s :Failed to unload module: %s",user->nick, parameters[0],ServerInstance->ModuleError());
return CMD_FAILURE;
}
if (!ServerInstance->LoadModule(parameters[0]))
{
user->WriteServ("974 %s %s :Failed to load module: %s",user->nick, parameters[0],ServerInstance->ModuleError());
return CMD_FAILURE;
}
ServerInstance->WriteOpers("*** MODULE '%s' GLOBALLY RELOADED BY '%s'",parameters[0],user->nick);
user->WriteServ("975 %s %s :Module successfully loaded.",user->nick, parameters[0]);
return CMD_SUCCESS;
}
};
class ModuleGlobalLoad : public Module
{
cmd_gloadmodule *mycommand;
cmd_gunloadmodule *mycommand2;
cmd_greloadmodule *mycommand3;
public:
ModuleGlobalLoad(InspIRCd* Me) : Module(Me)
{
mycommand = new cmd_gloadmodule(ServerInstance);
mycommand2 = new cmd_gunloadmodule(ServerInstance);
mycommand3 = new cmd_greloadmodule(ServerInstance);
ServerInstance->AddCommand(mycommand);
ServerInstance->AddCommand(mycommand2);
ServerInstance->AddCommand(mycommand3);
}
virtual ~ModuleGlobalLoad()
{
}
virtual Version GetVersion()
{
return Version(1, 1, 0, 0, VF_VENDOR, API_VERSION);
}
};
MODULE_INIT(ModuleGlobalLoad)
\ No newline at end of file +/* +------------------------------------+ + * | 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. + * + * --------------------------------------------------- + */ + +/* $ModDesc: Allows global loading of a module. */ + +#include "inspircd.h" +#include "users.h" +#include "channels.h" +#include "modules.h" + +/** Handle /GLOADMODULE + */ +class cmd_gloadmodule : public command_t +{ + public: + cmd_gloadmodule (InspIRCd* Instance) : command_t(Instance,"GLOADMODULE", 'o', 1) + { + this->source = "m_globalload.so"; + syntax = "<modulename>"; + } + + CmdResult Handle (const char** parameters, int pcnt, userrec *user) + { + if (ServerInstance->LoadModule(parameters[0])) + { + ServerInstance->WriteOpers("*** NEW MODULE '%s' GLOBALLY LOADED BY '%s'",parameters[0],user->nick); + user->WriteServ("975 %s %s :Module successfully loaded.",user->nick, parameters[0]); + + /* route it! */ + return CMD_SUCCESS; + } + else + { + user->WriteServ("974 %s %s :Failed to load module: %s",user->nick, parameters[0],ServerInstance->ModuleError()); + /* XXX - returning CMD_FAILURE here could potentially mean half the net loads it, half doesn't. pass it on anyway? -- w00t + * + * Returning CMD_SUCCESS would have the same effect, just with less servers. Someone should update this module to properly + * pass the success/failure for each server to the caller (or to all opers) -Special */ + return CMD_FAILURE; + } + } +}; + +/** Handle /GUNLOADMODULE + */ +class cmd_gunloadmodule : public command_t +{ + public: + cmd_gunloadmodule (InspIRCd* Instance) : command_t(Instance,"GUNLOADMODULE", 'o', 1) + { + this->source = "m_globalload.so"; + syntax = "<modulename>"; + } + + CmdResult Handle (const char** parameters, int pcnt, userrec *user) + { + if (ServerInstance->UnloadModule(parameters[0])) + { + ServerInstance->WriteOpers("*** MODULE '%s' GLOBALLY UNLOADED BY '%s'",parameters[0],user->nick); + user->WriteServ("973 %s %s :Module successfully unloaded.",user->nick, parameters[0]); + } + else + { + /* Return CMD_SUCCESS so the module will be unloaded on any servers it is loaded on - this is a seperate case entirely from loading -Special */ + user->WriteServ("972 %s %s :Failed to unload module: %s",user->nick, parameters[0],ServerInstance->ModuleError()); + } + return CMD_SUCCESS; + } +}; + +/** Handle /GRELOADMODULE + */ +class cmd_greloadmodule : public command_t +{ + public: + cmd_greloadmodule (InspIRCd* Instance) : command_t(Instance, "GRELOADMODULE", 'o', 1) + { + this->source = "m_globalload.so"; + syntax = "<modulename>"; + } + + CmdResult Handle(const char** parameters, int pcnt, userrec *user) + { + if (!ServerInstance->UnloadModule(parameters[0])) + { + user->WriteServ("972 %s %s :Failed to unload module: %s",user->nick, parameters[0],ServerInstance->ModuleError()); + return CMD_FAILURE; + } + + if (!ServerInstance->LoadModule(parameters[0])) + { + user->WriteServ("974 %s %s :Failed to load module: %s",user->nick, parameters[0],ServerInstance->ModuleError()); + return CMD_FAILURE; + } + + ServerInstance->WriteOpers("*** MODULE '%s' GLOBALLY RELOADED BY '%s'",parameters[0],user->nick); + user->WriteServ("975 %s %s :Module successfully loaded.",user->nick, parameters[0]); + + return CMD_SUCCESS; + } +}; + +class ModuleGlobalLoad : public Module +{ + cmd_gloadmodule *mycommand; + cmd_gunloadmodule *mycommand2; + cmd_greloadmodule *mycommand3; + + public: + ModuleGlobalLoad(InspIRCd* Me) : Module(Me) + { + + mycommand = new cmd_gloadmodule(ServerInstance); + mycommand2 = new cmd_gunloadmodule(ServerInstance); + mycommand3 = new cmd_greloadmodule(ServerInstance); + ServerInstance->AddCommand(mycommand); + ServerInstance->AddCommand(mycommand2); + ServerInstance->AddCommand(mycommand3); + } + + virtual ~ModuleGlobalLoad() + { + } + + virtual Version GetVersion() + { + return Version(1, 1, 0, 0, VF_VENDOR, API_VERSION); + } +}; + +MODULE_INIT(ModuleGlobalLoad) diff --git a/src/modules/m_globops.cpp b/src/modules/m_globops.cpp index 5745cc9c6..1a49858e2 100644 --- a/src/modules/m_globops.cpp +++ b/src/modules/m_globops.cpp @@ -1 +1,76 @@ -/* +------------------------------------+
* | 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.
*
* ---------------------------------------------------
*/
// Globops and +g support module by C.J.Edwards
#include "inspircd.h"
#include "users.h"
#include "channels.h"
#include "modules.h"
/* $ModDesc: Provides support for GLOBOPS and user mode +g */
/** Handle /GLOBOPS
*/
class cmd_globops : public command_t
{
public:
cmd_globops (InspIRCd* Instance) : command_t(Instance,"GLOBOPS",'o',1)
{
this->source = "m_globops.so";
syntax = "<any-text>";
}
CmdResult Handle (const char** parameters, int pcnt, userrec *user)
{
std::string line = "From " + std::string(user->nick) + ": ";
for (int i = 0; i < pcnt; i++)
{
line = line + std::string(parameters[i]) + " ";
}
ServerInstance->SNO->WriteToSnoMask('g',line);
/* route it (ofc :p) */
return CMD_SUCCESS;
}
};
class ModuleGlobops : public Module
{
cmd_globops* mycommand;
public:
ModuleGlobops(InspIRCd* Me)
: Module(Me)
{
mycommand = new cmd_globops(ServerInstance);
ServerInstance->AddCommand(mycommand);
ServerInstance->SNO->EnableSnomask('g',"GLOBOPS");
}
virtual ~ModuleGlobops()
{
ServerInstance->SNO->DisableSnomask('g');
DELETE(mycommand);
}
virtual Version GetVersion()
{
return Version(1, 1, 0, 1, VF_COMMON | VF_VENDOR, API_VERSION);
}
void Implements(char* List)
{
}
};
MODULE_INIT(ModuleGlobops)
\ No newline at end of file +/* +------------------------------------+ + * | 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. + * + * --------------------------------------------------- + */ + +// Globops and +g support module by C.J.Edwards + +#include "inspircd.h" +#include "users.h" +#include "channels.h" +#include "modules.h" + +/* $ModDesc: Provides support for GLOBOPS and user mode +g */ + +/** Handle /GLOBOPS + */ +class cmd_globops : public command_t +{ + public: + cmd_globops (InspIRCd* Instance) : command_t(Instance,"GLOBOPS",'o',1) + { + this->source = "m_globops.so"; + syntax = "<any-text>"; + } + + CmdResult Handle (const char** parameters, int pcnt, userrec *user) + { + std::string line = "From " + std::string(user->nick) + ": "; + for (int i = 0; i < pcnt; i++) + { + line = line + std::string(parameters[i]) + " "; + } + ServerInstance->SNO->WriteToSnoMask('g',line); + + /* route it (ofc :p) */ + return CMD_SUCCESS; + } +}; + +class ModuleGlobops : public Module +{ + cmd_globops* mycommand; + public: + ModuleGlobops(InspIRCd* Me) + : Module(Me) + { + mycommand = new cmd_globops(ServerInstance); + ServerInstance->AddCommand(mycommand); + ServerInstance->SNO->EnableSnomask('g',"GLOBOPS"); + } + + virtual ~ModuleGlobops() + { + ServerInstance->SNO->DisableSnomask('g'); + DELETE(mycommand); + } + + virtual Version GetVersion() + { + return Version(1, 1, 0, 1, VF_COMMON | VF_VENDOR, API_VERSION); + } + + void Implements(char* List) + { + } +}; + +MODULE_INIT(ModuleGlobops) diff --git a/src/modules/m_hash.h b/src/modules/m_hash.h index d82104cdb..ee9ead21c 100644 --- a/src/modules/m_hash.h +++ b/src/modules/m_hash.h @@ -1 +1,196 @@ -/* +------------------------------------+
* | 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.
*
* ---------------------------------------------------
*/
#ifndef __HASH_H__
#define __HASH_H__
#include "modules.h"
#define SHA256_DIGEST_SIZE (256 / 8)
#define SHA256_BLOCK_SIZE (512 / 8)
/** HashRequest is the base class used to send Hash requests to hashing.so.
* You should not instantiate classes of type HashRequest directly, instead
* you should instantiate classes of type HashResetRequest, HashSumRequest,
* HashKeyRequest and HashHexRequest, shown below.
*/
class HashRequest : public Request
{
/** The keys (IV) to use */
unsigned int* keys;
/** The output characters (hex sequence) to use */
const char* outputs;
/** The string to hash */
std::string tohash;
public:
/** Initialize HashRequest as an Hash_RESET message */
HashRequest(const char* req, Module* Me, Module* Target) : Request(Me, Target, req)
{
}
/** Initialize HashRequest as an Hash_SUM message */
HashRequest(Module* Me, Module* Target, const std::string &hashable) : Request(Me, Target, "SUM"), keys(NULL), outputs(NULL), tohash(hashable)
{
}
/** Initialize HashRequest as an Hash_KEY message */
HashRequest(Module* Me, Module* Target, unsigned int* k) : Request(Me, Target, "KEY"), keys(k), outputs(NULL), tohash("")
{
}
/** Initialize HashRequest as an Hash_HEX message */
HashRequest(Module* Me, Module* Target, const char* out) : Request(Me, Target, "HEX"), keys(NULL), outputs(out), tohash("")
{
}
/** Get data to be hashed */
const char* GetHashData()
{
return tohash.c_str();
}
/** Get keys (IVs) to be used */
unsigned int* GetKeyData()
{
return keys;
}
/** Get output characters (hex sequence) to be used */
const char* GetOutputs()
{
return outputs;
}
};
/** Send this class to the hashing module to query for its name.
*
* Example:
* \code
* cout << "Using hash algorithm: " << HashNameRequest(this, HashModule).Send();
* \endcode
*/
class HashNameRequest : public HashRequest
{
public:
/** Initialize HashNameRequest for sending.
* @param Me A pointer to the sending module
* @param Target A pointer to the hashing module
*/
HashNameRequest(Module* Me, Module* Target) : HashRequest("NAME", Me, Target)
{
}
};
/** Send this class to the hashing module to reset the Hash module to a known state.
* This will reset the IV to the defaults specified by the Hash spec,
* and reset the hex sequence to "0123456789abcdef". It should be sent before
* ANY other Request types.
*
* Example:
* \code
* // Reset the Hash module.
* HashResetRequest(this, HashModule).Send();
* \endcode
*/
class HashResetRequest : public HashRequest
{
public:
/** Initialize HashResetRequest for sending.
* @param Me A pointer to the sending module
* @param Target A pointer to the hashing module
*/
HashResetRequest(Module* Me, Module* Target) : HashRequest("RESET", Me, Target)
{
}
};
/** Send this class to the hashing module to HashSUM a std::string.
* You should make sure you know the state of the module before you send this
* class, e.g. by first sending an HashResetRequest class. The hash will be
* returned when you call Send().
*
* Example:
* \code
* // ALWAYS ALWAYS reset first, or set your own IV and hex chars.
* HashResetRequest(this, HashModule).Send();
* // Get the Hash sum of the string 'doodads'.
* std::string result = HashSumRequest(this, HashModule, "doodads").Send();
* \endcode
*/
class HashSumRequest : public HashRequest
{
public:
/** Initialize HashSumRequest for sending.
* @param Me A pointer to the sending module
* @param Target A pointer to the hashing module
* @param data The data to be hashed
*/
HashSumRequest(Module* Me, Module* Target, const std::string &data) : HashRequest(Me, Target, data)
{
}
};
/** Send this class to hashing module to change the IVs (keys) to use for hashing.
* You should make sure you know the state of the module before you send this
* class, e.g. by first sending an HashResetRequest class. The default values for
* the IV's are those specified in the Hash specification. Only in very special
* circumstances should you need to change the IV's (see for example m_cloaking.cpp)
*
* Example:
* \code
* unsigned int iv[] = { 0xFFFFFFFF, 0x00000000, 0xAAAAAAAA, 0xCCCCCCCC };
* HashKeyRequest(this, HashModule, iv);
* \endcode
*/
class HashKeyRequest : public HashRequest
{
public:
/** Initialize HashKeyRequest for sending.
* @param Me A pointer to the sending module
* @param Target A pointer to the hashing module
* @param data The new IV's. This should be an array of exactly four 32 bit values.
* On 64-bit architectures, the upper 32 bits of the values will be discarded.
*/
HashKeyRequest(Module* Me, Module* Target, unsigned int* data) : HashRequest(Me, Target, data)
{
}
};
/** Send this class to the hashing module to change the hex sequence to use for generating the returned value.
* You should make sure you know the state of the module before you send this
* class, e.g. by first sending an HashResetRequest class. The default value for
* the hex sequence is "0123456789abcdef". Only in very special circumstances should
* you need to change the hex sequence (see for example m_cloaking.cpp).
*
* Example:
* \code
* static const char tab[] = "fedcba9876543210";
* HashHexRequest(this, HashModule, tab);
* \endcode
*/
class HashHexRequest : public HashRequest
{
public:
/** Initialize HashHexRequest for sending.
* @param Me A pointer to the sending module
* @param Target A pointer to the hashing module
* @param data The hex sequence to use. This should contain exactly 16 ASCII characters,
* terminated by a NULL char.
*/
HashHexRequest(Module* Me, Module* Target, const char* data) : HashRequest(Me, Target, data)
{
}
};
#endif
\ No newline at end of file +/* +------------------------------------+ + * | 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. + * + * --------------------------------------------------- + */ + +#ifndef __HASH_H__ +#define __HASH_H__ + +#include "modules.h" + +#define SHA256_DIGEST_SIZE (256 / 8) +#define SHA256_BLOCK_SIZE (512 / 8) + +/** HashRequest is the base class used to send Hash requests to hashing.so. + * You should not instantiate classes of type HashRequest directly, instead + * you should instantiate classes of type HashResetRequest, HashSumRequest, + * HashKeyRequest and HashHexRequest, shown below. + */ +class HashRequest : public Request +{ + /** The keys (IV) to use */ + unsigned int* keys; + /** The output characters (hex sequence) to use */ + const char* outputs; + /** The string to hash */ + std::string tohash; + public: + /** Initialize HashRequest as an Hash_RESET message */ + HashRequest(const char* req, Module* Me, Module* Target) : Request(Me, Target, req) + { + } + + /** Initialize HashRequest as an Hash_SUM message */ + HashRequest(Module* Me, Module* Target, const std::string &hashable) : Request(Me, Target, "SUM"), keys(NULL), outputs(NULL), tohash(hashable) + { + } + + /** Initialize HashRequest as an Hash_KEY message */ + HashRequest(Module* Me, Module* Target, unsigned int* k) : Request(Me, Target, "KEY"), keys(k), outputs(NULL), tohash("") + { + } + + /** Initialize HashRequest as an Hash_HEX message */ + HashRequest(Module* Me, Module* Target, const char* out) : Request(Me, Target, "HEX"), keys(NULL), outputs(out), tohash("") + { + } + + /** Get data to be hashed */ + const char* GetHashData() + { + return tohash.c_str(); + } + + /** Get keys (IVs) to be used */ + unsigned int* GetKeyData() + { + return keys; + } + + /** Get output characters (hex sequence) to be used */ + const char* GetOutputs() + { + return outputs; + } +}; + +/** Send this class to the hashing module to query for its name. + * + * Example: + * \code + * cout << "Using hash algorithm: " << HashNameRequest(this, HashModule).Send(); + * \endcode + */ +class HashNameRequest : public HashRequest +{ + public: + /** Initialize HashNameRequest for sending. + * @param Me A pointer to the sending module + * @param Target A pointer to the hashing module + */ + HashNameRequest(Module* Me, Module* Target) : HashRequest("NAME", Me, Target) + { + } +}; + +/** Send this class to the hashing module to reset the Hash module to a known state. + * This will reset the IV to the defaults specified by the Hash spec, + * and reset the hex sequence to "0123456789abcdef". It should be sent before + * ANY other Request types. + * + * Example: + * \code + * // Reset the Hash module. + * HashResetRequest(this, HashModule).Send(); + * \endcode + */ +class HashResetRequest : public HashRequest +{ + public: + /** Initialize HashResetRequest for sending. + * @param Me A pointer to the sending module + * @param Target A pointer to the hashing module + */ + HashResetRequest(Module* Me, Module* Target) : HashRequest("RESET", Me, Target) + { + } +}; + +/** Send this class to the hashing module to HashSUM a std::string. + * You should make sure you know the state of the module before you send this + * class, e.g. by first sending an HashResetRequest class. The hash will be + * returned when you call Send(). + * + * Example: + * \code + * // ALWAYS ALWAYS reset first, or set your own IV and hex chars. + * HashResetRequest(this, HashModule).Send(); + * // Get the Hash sum of the string 'doodads'. + * std::string result = HashSumRequest(this, HashModule, "doodads").Send(); + * \endcode + */ +class HashSumRequest : public HashRequest +{ + public: + /** Initialize HashSumRequest for sending. + * @param Me A pointer to the sending module + * @param Target A pointer to the hashing module + * @param data The data to be hashed + */ + HashSumRequest(Module* Me, Module* Target, const std::string &data) : HashRequest(Me, Target, data) + { + } +}; + +/** Send this class to hashing module to change the IVs (keys) to use for hashing. + * You should make sure you know the state of the module before you send this + * class, e.g. by first sending an HashResetRequest class. The default values for + * the IV's are those specified in the Hash specification. Only in very special + * circumstances should you need to change the IV's (see for example m_cloaking.cpp) + * + * Example: + * \code + * unsigned int iv[] = { 0xFFFFFFFF, 0x00000000, 0xAAAAAAAA, 0xCCCCCCCC }; + * HashKeyRequest(this, HashModule, iv); + * \endcode + */ +class HashKeyRequest : public HashRequest +{ + public: + /** Initialize HashKeyRequest for sending. + * @param Me A pointer to the sending module + * @param Target A pointer to the hashing module + * @param data The new IV's. This should be an array of exactly four 32 bit values. + * On 64-bit architectures, the upper 32 bits of the values will be discarded. + */ + HashKeyRequest(Module* Me, Module* Target, unsigned int* data) : HashRequest(Me, Target, data) + { + } +}; + +/** Send this class to the hashing module to change the hex sequence to use for generating the returned value. + * You should make sure you know the state of the module before you send this + * class, e.g. by first sending an HashResetRequest class. The default value for + * the hex sequence is "0123456789abcdef". Only in very special circumstances should + * you need to change the hex sequence (see for example m_cloaking.cpp). + * + * Example: + * \code + * static const char tab[] = "fedcba9876543210"; + * HashHexRequest(this, HashModule, tab); + * \endcode + */ +class HashHexRequest : public HashRequest +{ + public: + /** Initialize HashHexRequest for sending. + * @param Me A pointer to the sending module + * @param Target A pointer to the hashing module + * @param data The hex sequence to use. This should contain exactly 16 ASCII characters, + * terminated by a NULL char. + */ + HashHexRequest(Module* Me, Module* Target, const char* data) : HashRequest(Me, Target, data) + { + } +}; + +#endif + diff --git a/src/modules/m_helpop.cpp b/src/modules/m_helpop.cpp index 965194a08..341f2b861 100644 --- a/src/modules/m_helpop.cpp +++ b/src/modules/m_helpop.cpp @@ -1 +1,191 @@ -/* +------------------------------------+
* | 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 "users.h"
#include "channels.h"
#include "modules.h"
/* $ModDesc: /helpop Command, Works like Unreal helpop */
static std::map<irc::string, std::string> helpop_map;
/** Handles user mode +h
*/
class Helpop : public ModeHandler
{
public:
Helpop(InspIRCd* Instance) : ModeHandler(Instance, 'h', 0, 0, false, MODETYPE_USER, true) { }
ModeAction OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string ¶meter, bool adding)
{
if (adding)
{
if (!dest->IsModeSet('h'))
{
dest->SetMode('h',true);
return MODEACTION_ALLOW;
}
}
else
{
if (dest->IsModeSet('h'))
{
dest->SetMode('h',false);
return MODEACTION_ALLOW;
}
}
return MODEACTION_DENY;
}
};
/** Handles /HELPOP
*/
class cmd_helpop : public command_t
{
public:
cmd_helpop (InspIRCd* Instance) : command_t(Instance, "HELPOP", 0, 0)
{
this->source = "m_helpop.so";
syntax = "<any-text>";
}
CmdResult Handle (const char** parameters, int pcnt, userrec *user)
{
irc::string parameter;
if (pcnt > 0)
parameter = parameters[0];
if (pcnt == 0 || parameter == "index")
{
/* iterate over all helpop items */
user->WriteServ("NOTICE %s :HELPOP topic index", user->nick);
for (std::map<irc::string, std::string>::iterator iter = helpop_map.begin(); iter != helpop_map.end(); iter++)
{
user->WriteServ("NOTICE %s : %s", user->nick, iter->first.c_str());
}
user->WriteServ("NOTICE %s :*** End of HELPOP topic index", user->nick);
}
else
{
user->WriteServ("NOTICE %s :*** HELPOP for %s", user->nick, parameters[0]);
std::map<irc::string, std::string>::iterator iter = helpop_map.find(parameter);
if (iter == helpop_map.end())
{
iter = helpop_map.find("nohelp");
}
std::string value = iter->second;
irc::sepstream stream(value, '\n');
std::string token = "*";
while ((token = stream.GetToken()) != "")
{
user->WriteServ("NOTICE %s :%s", user->nick, token.c_str());
}
user->WriteServ("NOTICE %s :*** End of HELPOP", user->nick);
}
/* We dont want these going out over the network, return CMD_FAILURE
* to make sure the protocol module thinks theyre not worth sending.
*/
return CMD_FAILURE;
}
};
class ModuleHelpop : public Module
{
private:
std::string h_file;
cmd_helpop* mycommand;
Helpop* ho;
public:
ModuleHelpop(InspIRCd* Me)
: Module(Me)
{
ReadConfig();
ho = new Helpop(ServerInstance);
if (!ServerInstance->AddMode(ho, 'h'))
throw ModuleException("Could not add new modes!");
mycommand = new cmd_helpop(ServerInstance);
ServerInstance->AddCommand(mycommand);
}
virtual void ReadConfig()
{
ConfigReader *MyConf = new ConfigReader(ServerInstance);
helpop_map.clear();
for (int i = 0; i < MyConf->Enumerate("helpop"); i++)
{
irc::string key = assign(MyConf->ReadValue("helpop", "key", i));
std::string value = MyConf->ReadValue("helpop", "value", i, true); /* Linefeeds allowed! */
if (key == "index")
{
throw ModuleException("m_helpop: The key 'index' is reserved for internal purposes. Please remove it.");
}
helpop_map[key] = value;
}
if (helpop_map.find("start") == helpop_map.end())
{
// error!
throw ModuleException("m_helpop: Helpop file is missing important entries. Please check the example conf.");
}
else if (helpop_map.find("nohelp") == helpop_map.end())
{
// error!
throw ModuleException("m_helpop: Helpop file is missing important entries. Please check the example conf.");
}
}
void Implements(char* List)
{
List[I_OnRehash] = List[I_OnWhois] = 1;
}
virtual void OnRehash(userrec* user, const std::string ¶meter)
{
ReadConfig();
}
virtual void OnWhois(userrec* src, userrec* dst)
{
if (dst->IsModeSet('h'))
{
ServerInstance->SendWhoisLine(src, dst, 310, std::string(src->nick)+" "+std::string(dst->nick)+" :is available for help.");
}
}
virtual ~ModuleHelpop()
{
ServerInstance->Modes->DelMode(ho);
DELETE(ho);
}
virtual Version GetVersion()
{
return Version(1,1,0,1,VF_COMMON|VF_VENDOR,API_VERSION);
}
};
MODULE_INIT(ModuleHelpop)
\ No newline at end of file +/* +------------------------------------+ + * | 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 "users.h" +#include "channels.h" +#include "modules.h" + +/* $ModDesc: /helpop Command, Works like Unreal helpop */ +static std::map<irc::string, std::string> helpop_map; + + +/** Handles user mode +h + */ +class Helpop : public ModeHandler +{ + public: + Helpop(InspIRCd* Instance) : ModeHandler(Instance, 'h', 0, 0, false, MODETYPE_USER, true) { } + + ModeAction OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string ¶meter, bool adding) + { + if (adding) + { + if (!dest->IsModeSet('h')) + { + dest->SetMode('h',true); + return MODEACTION_ALLOW; + } + } + else + { + if (dest->IsModeSet('h')) + { + dest->SetMode('h',false); + return MODEACTION_ALLOW; + } + } + + return MODEACTION_DENY; + } +}; + +/** Handles /HELPOP + */ +class cmd_helpop : public command_t +{ + public: + cmd_helpop (InspIRCd* Instance) : command_t(Instance, "HELPOP", 0, 0) + { + this->source = "m_helpop.so"; + syntax = "<any-text>"; + } + + CmdResult Handle (const char** parameters, int pcnt, userrec *user) + { + irc::string parameter; + if (pcnt > 0) + parameter = parameters[0]; + + if (pcnt == 0 || parameter == "index") + { + /* iterate over all helpop items */ + user->WriteServ("NOTICE %s :HELPOP topic index", user->nick); + for (std::map<irc::string, std::string>::iterator iter = helpop_map.begin(); iter != helpop_map.end(); iter++) + { + user->WriteServ("NOTICE %s : %s", user->nick, iter->first.c_str()); + } + user->WriteServ("NOTICE %s :*** End of HELPOP topic index", user->nick); + } + else + { + user->WriteServ("NOTICE %s :*** HELPOP for %s", user->nick, parameters[0]); + + std::map<irc::string, std::string>::iterator iter = helpop_map.find(parameter); + + if (iter == helpop_map.end()) + { + iter = helpop_map.find("nohelp"); + } + + std::string value = iter->second; + irc::sepstream stream(value, '\n'); + std::string token = "*"; + + while ((token = stream.GetToken()) != "") + { + user->WriteServ("NOTICE %s :%s", user->nick, token.c_str()); + } + + user->WriteServ("NOTICE %s :*** End of HELPOP", user->nick); + } + + /* We dont want these going out over the network, return CMD_FAILURE + * to make sure the protocol module thinks theyre not worth sending. + */ + return CMD_FAILURE; + } +}; + +class ModuleHelpop : public Module +{ + private: + std::string h_file; + cmd_helpop* mycommand; + Helpop* ho; + + public: + ModuleHelpop(InspIRCd* Me) + : Module(Me) + { + ReadConfig(); + ho = new Helpop(ServerInstance); + if (!ServerInstance->AddMode(ho, 'h')) + throw ModuleException("Could not add new modes!"); + mycommand = new cmd_helpop(ServerInstance); + ServerInstance->AddCommand(mycommand); + } + + virtual void ReadConfig() + { + ConfigReader *MyConf = new ConfigReader(ServerInstance); + + helpop_map.clear(); + + for (int i = 0; i < MyConf->Enumerate("helpop"); i++) + { + irc::string key = assign(MyConf->ReadValue("helpop", "key", i)); + std::string value = MyConf->ReadValue("helpop", "value", i, true); /* Linefeeds allowed! */ + + if (key == "index") + { + throw ModuleException("m_helpop: The key 'index' is reserved for internal purposes. Please remove it."); + } + + helpop_map[key] = value; + } + + if (helpop_map.find("start") == helpop_map.end()) + { + // error! + throw ModuleException("m_helpop: Helpop file is missing important entries. Please check the example conf."); + } + else if (helpop_map.find("nohelp") == helpop_map.end()) + { + // error! + throw ModuleException("m_helpop: Helpop file is missing important entries. Please check the example conf."); + } + + } + + void Implements(char* List) + { + List[I_OnRehash] = List[I_OnWhois] = 1; + } + + virtual void OnRehash(userrec* user, const std::string ¶meter) + { + ReadConfig(); + } + + virtual void OnWhois(userrec* src, userrec* dst) + { + if (dst->IsModeSet('h')) + { + ServerInstance->SendWhoisLine(src, dst, 310, std::string(src->nick)+" "+std::string(dst->nick)+" :is available for help."); + } + } + + virtual ~ModuleHelpop() + { + ServerInstance->Modes->DelMode(ho); + DELETE(ho); + } + + virtual Version GetVersion() + { + return Version(1,1,0,1,VF_COMMON|VF_VENDOR,API_VERSION); + } +}; + +MODULE_INIT(ModuleHelpop) diff --git a/src/modules/m_hidechans.cpp b/src/modules/m_hidechans.cpp index 3924b84b9..2c3769f7a 100644 --- a/src/modules/m_hidechans.cpp +++ b/src/modules/m_hidechans.cpp @@ -1 +1,95 @@ -/* +------------------------------------+
* | 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 "users.h"
#include "channels.h"
#include "modules.h"
/* $ModDesc: Provides support for hiding channels with user mode +I */
/** Handles user mode +I
*/
class HideChans : public ModeHandler
{
public:
HideChans(InspIRCd* Instance) : ModeHandler(Instance, 'I', 0, 0, false, MODETYPE_USER, false) { }
ModeAction OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string ¶meter, bool adding)
{
/* Only opers can change other users modes */
if (source != dest)
return MODEACTION_DENY;
if (adding)
{
if (!dest->IsModeSet('I'))
{
dest->SetMode('I',true);
return MODEACTION_ALLOW;
}
}
else
{
if (dest->IsModeSet('I'))
{
dest->SetMode('I',false);
return MODEACTION_ALLOW;
}
}
return MODEACTION_DENY;
}
};
class ModuleHideChans : public Module
{
HideChans* hm;
public:
ModuleHideChans(InspIRCd* Me)
: Module(Me)
{
hm = new HideChans(ServerInstance);
if (!ServerInstance->AddMode(hm, 'I'))
throw ModuleException("Could not add new modes!");
}
void Implements(char* List)
{
List[I_OnWhoisLine] = 1;
}
virtual ~ModuleHideChans()
{
ServerInstance->Modes->DelMode(hm);
DELETE(hm);
}
virtual Version GetVersion()
{
return Version(1,1,0,0,VF_COMMON|VF_VENDOR,API_VERSION);
}
int OnWhoisLine(userrec* user, userrec* dest, int &numeric, std::string &text)
{
/* Dont display channels if they have +I set and the
* person doing the WHOIS is not an oper
*/
return ((user != dest) && (!IS_OPER(user)) && (numeric == 319) && dest->IsModeSet('I'));
}
};
MODULE_INIT(ModuleHideChans)
\ No newline at end of file +/* +------------------------------------+ + * | 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 "users.h" +#include "channels.h" +#include "modules.h" + +/* $ModDesc: Provides support for hiding channels with user mode +I */ + +/** Handles user mode +I + */ +class HideChans : public ModeHandler +{ + public: + HideChans(InspIRCd* Instance) : ModeHandler(Instance, 'I', 0, 0, false, MODETYPE_USER, false) { } + + ModeAction OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string ¶meter, bool adding) + { + /* Only opers can change other users modes */ + if (source != dest) + return MODEACTION_DENY; + + if (adding) + { + if (!dest->IsModeSet('I')) + { + dest->SetMode('I',true); + return MODEACTION_ALLOW; + } + } + else + { + if (dest->IsModeSet('I')) + { + dest->SetMode('I',false); + return MODEACTION_ALLOW; + } + } + + return MODEACTION_DENY; + } +}; + +class ModuleHideChans : public Module +{ + + HideChans* hm; + public: + ModuleHideChans(InspIRCd* Me) + : Module(Me) + { + + hm = new HideChans(ServerInstance); + if (!ServerInstance->AddMode(hm, 'I')) + throw ModuleException("Could not add new modes!"); + } + + void Implements(char* List) + { + List[I_OnWhoisLine] = 1; + } + + virtual ~ModuleHideChans() + { + ServerInstance->Modes->DelMode(hm); + DELETE(hm); + } + + virtual Version GetVersion() + { + return Version(1,1,0,0,VF_COMMON|VF_VENDOR,API_VERSION); + } + + int OnWhoisLine(userrec* user, userrec* dest, int &numeric, std::string &text) + { + /* Dont display channels if they have +I set and the + * person doing the WHOIS is not an oper + */ + return ((user != dest) && (!IS_OPER(user)) && (numeric == 319) && dest->IsModeSet('I')); + } +}; + + +MODULE_INIT(ModuleHideChans) diff --git a/src/modules/m_hideoper.cpp b/src/modules/m_hideoper.cpp index c2b472bad..9f547d77d 100644 --- a/src/modules/m_hideoper.cpp +++ b/src/modules/m_hideoper.cpp @@ -1 +1,94 @@ -/* +------------------------------------+
* | 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 "users.h"
#include "channels.h"
#include "modules.h"
/* $ModDesc: Provides support for hiding oper status with user mode +H */
/** Handles user mode +B
*/
class HideOper : public ModeHandler
{
public:
HideOper(InspIRCd* Instance) : ModeHandler(Instance, 'H', 0, 0, false, MODETYPE_USER, true) { }
ModeAction OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string ¶meter, bool adding)
{
if (source != dest)
return MODEACTION_DENY;
if (adding)
{
if (!dest->IsModeSet('H'))
{
dest->SetMode('H',true);
return MODEACTION_ALLOW;
}
}
else
{
if (dest->IsModeSet('H'))
{
dest->SetMode('H',false);
return MODEACTION_ALLOW;
}
}
return MODEACTION_DENY;
}
};
class ModuleHideOper : public Module
{
HideOper* hm;
public:
ModuleHideOper(InspIRCd* Me)
: Module(Me)
{
hm = new HideOper(ServerInstance);
if (!ServerInstance->AddMode(hm, 'H'))
throw ModuleException("Could not add new modes!");
}
void Implements(char* List)
{
List[I_OnWhoisLine] = 1;
}
virtual ~ModuleHideOper()
{
ServerInstance->Modes->DelMode(hm);
DELETE(hm);
}
virtual Version GetVersion()
{
return Version(1,1,0,0,VF_COMMON|VF_VENDOR,API_VERSION);
}
int OnWhoisLine(userrec* user, userrec* dest, int &numeric, std::string &text)
{
/* Dont display numeric 313 (RPL_WHOISOPER) if they have +H set and the
* person doing the WHOIS is not an oper
*/
return ((!IS_OPER(user)) && (numeric == 313) && dest->IsModeSet('H'));
}
};
MODULE_INIT(ModuleHideOper)
\ No newline at end of file +/* +------------------------------------+ + * | 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 "users.h" +#include "channels.h" +#include "modules.h" + +/* $ModDesc: Provides support for hiding oper status with user mode +H */ + +/** Handles user mode +B + */ +class HideOper : public ModeHandler +{ + public: + HideOper(InspIRCd* Instance) : ModeHandler(Instance, 'H', 0, 0, false, MODETYPE_USER, true) { } + + ModeAction OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string ¶meter, bool adding) + { + if (source != dest) + return MODEACTION_DENY; + + if (adding) + { + if (!dest->IsModeSet('H')) + { + dest->SetMode('H',true); + return MODEACTION_ALLOW; + } + } + else + { + if (dest->IsModeSet('H')) + { + dest->SetMode('H',false); + return MODEACTION_ALLOW; + } + } + + return MODEACTION_DENY; + } +}; + +class ModuleHideOper : public Module +{ + + HideOper* hm; + public: + ModuleHideOper(InspIRCd* Me) + : Module(Me) + { + + hm = new HideOper(ServerInstance); + if (!ServerInstance->AddMode(hm, 'H')) + throw ModuleException("Could not add new modes!"); + } + + void Implements(char* List) + { + List[I_OnWhoisLine] = 1; + } + + virtual ~ModuleHideOper() + { + ServerInstance->Modes->DelMode(hm); + DELETE(hm); + } + + virtual Version GetVersion() + { + return Version(1,1,0,0,VF_COMMON|VF_VENDOR,API_VERSION); + } + + int OnWhoisLine(userrec* user, userrec* dest, int &numeric, std::string &text) + { + /* Dont display numeric 313 (RPL_WHOISOPER) if they have +H set and the + * person doing the WHOIS is not an oper + */ + return ((!IS_OPER(user)) && (numeric == 313) && dest->IsModeSet('H')); + } +}; + + +MODULE_INIT(ModuleHideOper) diff --git a/src/modules/m_hostchange.cpp b/src/modules/m_hostchange.cpp index f7ff58fa1..dc45a43d4 100644 --- a/src/modules/m_hostchange.cpp +++ b/src/modules/m_hostchange.cpp @@ -1 +1,148 @@ -/* +------------------------------------+
* | 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 "users.h"
#include "channels.h"
#include "modules.h"
/* $ModDesc: Provides masking of user hostnames in a different way to m_cloaking */
/** Holds information on a host set by m_hostchange
*/
class Host : public classbase
{
public:
std::string action;
std::string newhost;
};
typedef std::map<std::string,Host*> hostchanges_t;
class ModuleHostChange : public Module
{
private:
hostchanges_t hostchanges;
std::string MySuffix;
std::string MyPrefix;
std::string MySeparator;
public:
ModuleHostChange(InspIRCd* Me)
: Module(Me)
{
OnRehash(NULL,"");
}
virtual ~ModuleHostChange()
{
for (hostchanges_t::iterator i = hostchanges.begin(); i != hostchanges.end(); i++)
{
DELETE(i->second);
}
hostchanges.clear();
}
Priority Prioritize()
{
return (Priority)ServerInstance->PriorityAfter("m_cloaking.so");
}
void Implements(char* List)
{
List[I_OnRehash] = List[I_OnUserConnect] = 1;
}
virtual void OnRehash(userrec* user, const std::string ¶meter)
{
ConfigReader Conf(ServerInstance);
MySuffix = Conf.ReadValue("host","suffix",0);
MyPrefix = Conf.ReadValue("host","prefix","",0);
MySeparator = Conf.ReadValue("host","separator",".",0);
for (hostchanges_t::iterator i = hostchanges.begin(); i != hostchanges.end(); i++)
{
DELETE(i->second);
}
hostchanges.clear();
for (int index = 0; index < Conf.Enumerate("hostchange"); index++)
{
std::string mask = Conf.ReadValue("hostchange","mask",index);
std::string action = Conf.ReadValue("hostchange","action",index);
std::string newhost = Conf.ReadValue("hostchange","value",index);
Host* x = new Host;
x->action = action;
x->newhost = newhost;
hostchanges[mask] = x;
}
}
virtual Version GetVersion()
{
// returns the version number of the module to be
// listed in /MODULES
return Version(1,1,0,1,VF_VENDOR,API_VERSION);
}
virtual void OnUserConnect(userrec* user)
{
for (hostchanges_t::iterator i = hostchanges.begin(); i != hostchanges.end(); i++)
{
if (ServerInstance->MatchText(std::string(user->ident)+"@"+std::string(user->host),i->first))
{
Host* h = (Host*)i->second;
// host of new user matches a hostchange tag's mask
std::string newhost;
if (h->action == "set")
{
newhost = h->newhost;
}
else if (h->action == "suffix")
{
newhost = MySuffix;
}
else if (h->action == "addnick")
{
// first take their nick and strip out non-dns, leaving just [A-Z0-9\-]
std::string complete;
std::string old = user->nick;
for (unsigned int j = 0; j < old.length(); j++)
{
if (((old[j] >= 'A') && (old[j] <= 'Z')) ||
((old[j] >= 'a') && (old[j] <= 'z')) ||
((old[j] >= '0') && (old[j] <= '9')) ||
(old[j] == '-'))
{
complete = complete + old[j];
}
}
if (complete.empty())
complete = "i-have-a-lame-nick";
if (!MyPrefix.empty())
newhost = MyPrefix + MySeparator + complete;
else
newhost = complete + MySeparator + MySuffix;
}
if (!newhost.empty())
{
user->WriteServ("NOTICE "+std::string(user->nick)+" :Setting your virtual host: " + newhost);
if (!user->ChangeDisplayedHost(newhost.c_str()))
user->WriteServ("NOTICE "+std::string(user->nick)+" :Could not set your virtual host: " + newhost);
return;
}
}
}
}
};
MODULE_INIT(ModuleHostChange)
\ No newline at end of file +/* +------------------------------------+ + * | 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 "users.h" +#include "channels.h" +#include "modules.h" + +/* $ModDesc: Provides masking of user hostnames in a different way to m_cloaking */ + +/** Holds information on a host set by m_hostchange + */ +class Host : public classbase +{ + public: + std::string action; + std::string newhost; +}; + +typedef std::map<std::string,Host*> hostchanges_t; + +class ModuleHostChange : public Module +{ + private: + hostchanges_t hostchanges; + std::string MySuffix; + std::string MyPrefix; + std::string MySeparator; + + public: + ModuleHostChange(InspIRCd* Me) + : Module(Me) + { + OnRehash(NULL,""); + } + + virtual ~ModuleHostChange() + { + for (hostchanges_t::iterator i = hostchanges.begin(); i != hostchanges.end(); i++) + { + DELETE(i->second); + } + hostchanges.clear(); + } + + Priority Prioritize() + { + return (Priority)ServerInstance->PriorityAfter("m_cloaking.so"); + } + + void Implements(char* List) + { + List[I_OnRehash] = List[I_OnUserConnect] = 1; + } + + virtual void OnRehash(userrec* user, const std::string ¶meter) + { + ConfigReader Conf(ServerInstance); + MySuffix = Conf.ReadValue("host","suffix",0); + MyPrefix = Conf.ReadValue("host","prefix","",0); + MySeparator = Conf.ReadValue("host","separator",".",0); + for (hostchanges_t::iterator i = hostchanges.begin(); i != hostchanges.end(); i++) + { + DELETE(i->second); + } + hostchanges.clear(); + for (int index = 0; index < Conf.Enumerate("hostchange"); index++) + { + std::string mask = Conf.ReadValue("hostchange","mask",index); + std::string action = Conf.ReadValue("hostchange","action",index); + std::string newhost = Conf.ReadValue("hostchange","value",index); + Host* x = new Host; + x->action = action; + x->newhost = newhost; + hostchanges[mask] = x; + } + } + + virtual Version GetVersion() + { + // returns the version number of the module to be + // listed in /MODULES + return Version(1,1,0,1,VF_VENDOR,API_VERSION); + } + + virtual void OnUserConnect(userrec* user) + { + for (hostchanges_t::iterator i = hostchanges.begin(); i != hostchanges.end(); i++) + { + if (ServerInstance->MatchText(std::string(user->ident)+"@"+std::string(user->host),i->first)) + { + Host* h = (Host*)i->second; + // host of new user matches a hostchange tag's mask + std::string newhost; + if (h->action == "set") + { + newhost = h->newhost; + } + else if (h->action == "suffix") + { + newhost = MySuffix; + } + else if (h->action == "addnick") + { + // first take their nick and strip out non-dns, leaving just [A-Z0-9\-] + std::string complete; + std::string old = user->nick; + for (unsigned int j = 0; j < old.length(); j++) + { + if (((old[j] >= 'A') && (old[j] <= 'Z')) || + ((old[j] >= 'a') && (old[j] <= 'z')) || + ((old[j] >= '0') && (old[j] <= '9')) || + (old[j] == '-')) + { + complete = complete + old[j]; + } + } + if (complete.empty()) + complete = "i-have-a-lame-nick"; + + if (!MyPrefix.empty()) + newhost = MyPrefix + MySeparator + complete; + else + newhost = complete + MySeparator + MySuffix; + } + if (!newhost.empty()) + { + user->WriteServ("NOTICE "+std::string(user->nick)+" :Setting your virtual host: " + newhost); + if (!user->ChangeDisplayedHost(newhost.c_str())) + user->WriteServ("NOTICE "+std::string(user->nick)+" :Could not set your virtual host: " + newhost); + return; + } + } + } + } +}; + +MODULE_INIT(ModuleHostChange) diff --git a/src/modules/m_http_client.cpp b/src/modules/m_http_client.cpp index 3f9875caf..35b93b581 100644 --- a/src/modules/m_http_client.cpp +++ b/src/modules/m_http_client.cpp @@ -1 +1,346 @@ -/* +------------------------------------+
* | 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 "httpclient.h"
/* $ModDesc: HTTP client service provider */
class URL
{
public:
std::string url;
std::string protocol, username, password, domain, request;
int port;
};
class HTTPSocket : public InspSocket
{
private:
InspIRCd *Server;
class ModuleHTTPClient *Mod;
HTTPClientRequest req;
HTTPClientResponse *response;
URL url;
enum { HTTP_CLOSED, HTTP_REQSENT, HTTP_HEADERS, HTTP_DATA } status;
std::string data;
std::string buffer;
public:
HTTPSocket(InspIRCd *Instance, class ModuleHTTPClient *Mod);
virtual ~HTTPSocket();
virtual bool DoRequest(HTTPClientRequest *req);
virtual bool ParseURL(const std::string &url);
virtual void Connect(const std::string &ip);
virtual bool OnConnected();
virtual bool OnDataReady();
virtual void OnClose();
};
class HTTPResolver : public Resolver
{
private:
HTTPSocket *socket;
public:
HTTPResolver(HTTPSocket *socket, InspIRCd *Instance, const string &hostname, bool &cached, Module* me) : Resolver(Instance, hostname, DNS_QUERY_FORWARD, cached, me), socket(socket)
{
}
void OnLookupComplete(const string &result, unsigned int ttl, bool cached)
{
socket->Connect(result);
}
void OnError(ResolverError e, const string &errmsg)
{
delete socket;
}
};
typedef vector<HTTPSocket*> HTTPList;
class ModuleHTTPClient : public Module
{
public:
HTTPList sockets;
ModuleHTTPClient(InspIRCd *Me)
: Module(Me)
{
}
virtual ~ModuleHTTPClient()
{
for (HTTPList::iterator i = sockets.begin(); i != sockets.end(); i++)
delete *i;
}
virtual Version GetVersion()
{
return Version(1, 0, 0, 0, VF_SERVICEPROVIDER | VF_VENDOR, API_VERSION);
}
void Implements(char* List)
{
List[I_OnRequest] = 1;
}
char* OnRequest(Request *req)
{
HTTPClientRequest *httpreq = (HTTPClientRequest *)req;
if (!strcmp(httpreq->GetId(), HTTP_CLIENT_REQUEST))
{
HTTPSocket *sock = new HTTPSocket(ServerInstance, this);
sock->DoRequest(httpreq);
// No return value
}
return NULL;
}
};
HTTPSocket::HTTPSocket(InspIRCd *Instance, ModuleHTTPClient *Mod)
: InspSocket(Instance), Server(Instance), Mod(Mod), status(HTTP_CLOSED)
{
this->ClosePending = false;
this->port = 80;
}
HTTPSocket::~HTTPSocket()
{
Close();
for (HTTPList::iterator i = Mod->sockets.begin(); i != Mod->sockets.end(); i++)
{
if (*i == this)
{
Mod->sockets.erase(i);
break;
}
}
}
bool HTTPSocket::DoRequest(HTTPClientRequest *req)
{
/* Tweak by brain - we take a copy of this,
* so that the caller doesnt need to leave
* pointers knocking around, less chance of
* a memory leak.
*/
this->req = *req;
if (!ParseURL(this->req.GetURL()))
return false;
this->port = url.port;
strlcpy(this->host, url.domain.c_str(), MAXBUF);
in_addr addy1;
#ifdef IPV6
in6_addr addy2;
if ((inet_aton(this->host, &addy1) > 0) || (inet_pton(AF_INET6, this->host, &addy2) > 0))
#else
if (inet_aton(this->host, &addy1) > 0)
#endif
{
bool cached;
HTTPResolver* r = new HTTPResolver(this, Server, url.domain, cached, (Module*)Mod);
Instance->AddResolver(r, cached);
return true;
}
else
{
this->Connect(url.domain);
}
return true;
}
bool HTTPSocket::ParseURL(const std::string &iurl)
{
url.url = iurl;
url.port = 80;
url.protocol = "http";
irc::sepstream tokenizer(iurl, '/');
for (int p = 0;; p++)
{
std::string part = tokenizer.GetToken();
if (part.empty() && tokenizer.StreamEnd())
break;
if ((p == 0) && (part[part.length() - 1] == ':'))
{
// Protocol ('http:')
url.protocol = part.substr(0, part.length() - 1);
}
else if ((p == 1) && (part.empty()))
{
continue;
}
else if (url.domain.empty())
{
// Domain part: [user[:pass]@]domain[:port]
std::string::size_type usrpos = part.find('@');
if (usrpos != std::string::npos)
{
// Have a user (and possibly password) part
std::string::size_type ppos = part.find(':');
if ((ppos != std::string::npos) && (ppos < usrpos))
{
// Have password too
url.password = part.substr(ppos + 1, usrpos - ppos - 1);
url.username = part.substr(0, ppos);
}
else
{
url.username = part.substr(0, usrpos);
}
part = part.substr(usrpos + 1);
}
std::string::size_type popos = part.rfind(':');
if (popos != std::string::npos)
{
url.port = atoi(part.substr(popos + 1).c_str());
url.domain = part.substr(0, popos);
}
else
{
url.domain = part;
}
}
else
{
// Request (part of it)..
url.request.append("/");
url.request.append(part);
}
}
if (url.request.empty())
url.request = "/";
if ((url.domain.empty()) || (!url.port) || (url.protocol.empty()))
{
Instance->Log(DEFAULT, "Invalid URL (%s): Missing required value", iurl.c_str());
return false;
}
if (url.protocol != "http")
{
Instance->Log(DEFAULT, "Invalid URL (%s): Unsupported protocol '%s'", iurl.c_str(), url.protocol.c_str());
return false;
}
return true;
}
void HTTPSocket::Connect(const string &ip)
{
strlcpy(this->IP, ip.c_str(), MAXBUF);
if (!this->DoConnect())
{
delete this;
}
}
bool HTTPSocket::OnConnected()
{
std::string request = "GET " + url.request + " HTTP/1.1\r\n";
// Dump headers into the request
HeaderMap headers = req.GetHeaders();
for (HeaderMap::iterator i = headers.begin(); i != headers.end(); i++)
request += i->first + ": " + i->second + "\r\n";
// The Host header is required for HTTP 1.1 and isn't known when the request is created; if they didn't overload it
// manually, add it here
if (headers.find("Host") == headers.end())
request += "Host: " + url.domain + "\r\n";
request += "\r\n";
this->status = HTTP_REQSENT;
return this->Write(request);
}
bool HTTPSocket::OnDataReady()
{
char *data = this->Read();
if (!data)
{
this->Close();
return false;
}
if (this->status < HTTP_DATA)
{
std::string line;
std::string::size_type pos;
this->buffer += data;
while ((pos = buffer.find("\r\n")) != std::string::npos)
{
line = buffer.substr(0, pos);
buffer = buffer.substr(pos + 2);
if (line.empty())
{
this->status = HTTP_DATA;
this->data += this->buffer;
this->buffer.clear();
break;
}
if (this->status == HTTP_REQSENT)
{
// HTTP reply (HTTP/1.1 200 msg)
char const* data = line.c_str();
data += 9;
response = new HTTPClientResponse((Module*)Mod, req.GetSource() , url.url, atoi(data), data + 4);
this->status = HTTP_HEADERS;
continue;
}
if ((pos = line.find(':')) != std::string::npos)
{
response->AddHeader(line.substr(0, pos), line.substr(pos + 1));
}
else
{
continue;
}
}
}
else
{
this->data += data;
}
return true;
}
void HTTPSocket::OnClose()
{
if (data.empty())
return; // notification that request failed?
response->data = data;
response->Send();
delete response;
}
MODULE_INIT(ModuleHTTPClient)
\ No newline at end of file +/* +------------------------------------+ + * | 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 "httpclient.h" + +/* $ModDesc: HTTP client service provider */ + +class URL +{ + public: + std::string url; + std::string protocol, username, password, domain, request; + int port; +}; + +class HTTPSocket : public InspSocket +{ + private: + InspIRCd *Server; + class ModuleHTTPClient *Mod; + HTTPClientRequest req; + HTTPClientResponse *response; + URL url; + enum { HTTP_CLOSED, HTTP_REQSENT, HTTP_HEADERS, HTTP_DATA } status; + std::string data; + std::string buffer; + + public: + HTTPSocket(InspIRCd *Instance, class ModuleHTTPClient *Mod); + virtual ~HTTPSocket(); + virtual bool DoRequest(HTTPClientRequest *req); + virtual bool ParseURL(const std::string &url); + virtual void Connect(const std::string &ip); + virtual bool OnConnected(); + virtual bool OnDataReady(); + virtual void OnClose(); +}; + +class HTTPResolver : public Resolver +{ + private: + HTTPSocket *socket; + public: + HTTPResolver(HTTPSocket *socket, InspIRCd *Instance, const string &hostname, bool &cached, Module* me) : Resolver(Instance, hostname, DNS_QUERY_FORWARD, cached, me), socket(socket) + { + } + + void OnLookupComplete(const string &result, unsigned int ttl, bool cached) + { + socket->Connect(result); + } + + void OnError(ResolverError e, const string &errmsg) + { + delete socket; + } +}; + +typedef vector<HTTPSocket*> HTTPList; + +class ModuleHTTPClient : public Module +{ + public: + HTTPList sockets; + + ModuleHTTPClient(InspIRCd *Me) + : Module(Me) + { + } + + virtual ~ModuleHTTPClient() + { + for (HTTPList::iterator i = sockets.begin(); i != sockets.end(); i++) + delete *i; + } + + virtual Version GetVersion() + { + return Version(1, 0, 0, 0, VF_SERVICEPROVIDER | VF_VENDOR, API_VERSION); + } + + void Implements(char* List) + { + List[I_OnRequest] = 1; + } + + char* OnRequest(Request *req) + { + HTTPClientRequest *httpreq = (HTTPClientRequest *)req; + if (!strcmp(httpreq->GetId(), HTTP_CLIENT_REQUEST)) + { + HTTPSocket *sock = new HTTPSocket(ServerInstance, this); + sock->DoRequest(httpreq); + // No return value + } + return NULL; + } +}; + +HTTPSocket::HTTPSocket(InspIRCd *Instance, ModuleHTTPClient *Mod) + : InspSocket(Instance), Server(Instance), Mod(Mod), status(HTTP_CLOSED) +{ + this->ClosePending = false; + this->port = 80; +} + +HTTPSocket::~HTTPSocket() +{ + Close(); + for (HTTPList::iterator i = Mod->sockets.begin(); i != Mod->sockets.end(); i++) + { + if (*i == this) + { + Mod->sockets.erase(i); + break; + } + } +} + +bool HTTPSocket::DoRequest(HTTPClientRequest *req) +{ + /* Tweak by brain - we take a copy of this, + * so that the caller doesnt need to leave + * pointers knocking around, less chance of + * a memory leak. + */ + this->req = *req; + + if (!ParseURL(this->req.GetURL())) + return false; + + this->port = url.port; + strlcpy(this->host, url.domain.c_str(), MAXBUF); + + in_addr addy1; +#ifdef IPV6 + in6_addr addy2; + if ((inet_aton(this->host, &addy1) > 0) || (inet_pton(AF_INET6, this->host, &addy2) > 0)) +#else + if (inet_aton(this->host, &addy1) > 0) +#endif + { + bool cached; + HTTPResolver* r = new HTTPResolver(this, Server, url.domain, cached, (Module*)Mod); + Instance->AddResolver(r, cached); + return true; + } + else + { + this->Connect(url.domain); + } + + return true; +} + +bool HTTPSocket::ParseURL(const std::string &iurl) +{ + url.url = iurl; + url.port = 80; + url.protocol = "http"; + + irc::sepstream tokenizer(iurl, '/'); + + for (int p = 0;; p++) + { + std::string part = tokenizer.GetToken(); + if (part.empty() && tokenizer.StreamEnd()) + break; + + if ((p == 0) && (part[part.length() - 1] == ':')) + { + // Protocol ('http:') + url.protocol = part.substr(0, part.length() - 1); + } + else if ((p == 1) && (part.empty())) + { + continue; + } + else if (url.domain.empty()) + { + // Domain part: [user[:pass]@]domain[:port] + std::string::size_type usrpos = part.find('@'); + if (usrpos != std::string::npos) + { + // Have a user (and possibly password) part + std::string::size_type ppos = part.find(':'); + if ((ppos != std::string::npos) && (ppos < usrpos)) + { + // Have password too + url.password = part.substr(ppos + 1, usrpos - ppos - 1); + url.username = part.substr(0, ppos); + } + else + { + url.username = part.substr(0, usrpos); + } + + part = part.substr(usrpos + 1); + } + + std::string::size_type popos = part.rfind(':'); + if (popos != std::string::npos) + { + url.port = atoi(part.substr(popos + 1).c_str()); + url.domain = part.substr(0, popos); + } + else + { + url.domain = part; + } + } + else + { + // Request (part of it).. + url.request.append("/"); + url.request.append(part); + } + } + + if (url.request.empty()) + url.request = "/"; + + if ((url.domain.empty()) || (!url.port) || (url.protocol.empty())) + { + Instance->Log(DEFAULT, "Invalid URL (%s): Missing required value", iurl.c_str()); + return false; + } + + if (url.protocol != "http") + { + Instance->Log(DEFAULT, "Invalid URL (%s): Unsupported protocol '%s'", iurl.c_str(), url.protocol.c_str()); + return false; + } + + return true; +} + +void HTTPSocket::Connect(const string &ip) +{ + strlcpy(this->IP, ip.c_str(), MAXBUF); + + if (!this->DoConnect()) + { + delete this; + } +} + +bool HTTPSocket::OnConnected() +{ + std::string request = "GET " + url.request + " HTTP/1.1\r\n"; + + // Dump headers into the request + HeaderMap headers = req.GetHeaders(); + + for (HeaderMap::iterator i = headers.begin(); i != headers.end(); i++) + request += i->first + ": " + i->second + "\r\n"; + + // The Host header is required for HTTP 1.1 and isn't known when the request is created; if they didn't overload it + // manually, add it here + if (headers.find("Host") == headers.end()) + request += "Host: " + url.domain + "\r\n"; + + request += "\r\n"; + + this->status = HTTP_REQSENT; + + return this->Write(request); +} + +bool HTTPSocket::OnDataReady() +{ + char *data = this->Read(); + + if (!data) + { + this->Close(); + return false; + } + + if (this->status < HTTP_DATA) + { + std::string line; + std::string::size_type pos; + + this->buffer += data; + while ((pos = buffer.find("\r\n")) != std::string::npos) + { + line = buffer.substr(0, pos); + buffer = buffer.substr(pos + 2); + if (line.empty()) + { + this->status = HTTP_DATA; + this->data += this->buffer; + this->buffer.clear(); + break; + } + + if (this->status == HTTP_REQSENT) + { + // HTTP reply (HTTP/1.1 200 msg) + char const* data = line.c_str(); + data += 9; + response = new HTTPClientResponse((Module*)Mod, req.GetSource() , url.url, atoi(data), data + 4); + this->status = HTTP_HEADERS; + continue; + } + + if ((pos = line.find(':')) != std::string::npos) + { + response->AddHeader(line.substr(0, pos), line.substr(pos + 1)); + } + else + { + continue; + } + } + } + else + { + this->data += data; + } + return true; +} + +void HTTPSocket::OnClose() +{ + if (data.empty()) + return; // notification that request failed? + + response->data = data; + response->Send(); + delete response; +} + +MODULE_INIT(ModuleHTTPClient) diff --git a/src/modules/m_httpd.cpp b/src/modules/m_httpd.cpp index 6ff80ad80..8494863a3 100644 --- a/src/modules/m_httpd.cpp +++ b/src/modules/m_httpd.cpp @@ -1 +1,419 @@ -/* +------------------------------------+
* | 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 <algorithm>
#include "modules.h"
#include "httpd.h"
/* $ModDesc: Provides HTTP serving facilities to modules */
class ModuleHttpServer;
static ModuleHttpServer* HttpModule;
static bool claimed;
/** HTTP socket states
*/
enum HttpState
{
HTTP_LISTEN = 0,
HTTP_SERVE_WAIT_REQUEST = 1,
HTTP_SERVE_RECV_POSTDATA = 2,
HTTP_SERVE_SEND_DATA = 3
};
class HttpServerSocket;
/** This class is used to handle HTTP socket timeouts
*/
class HttpServerTimeout : public InspTimer
{
private:
/** HttpServerSocket we are attached to
*/
HttpServerSocket* s;
/** Socketengine the file descriptor is in
*/
SocketEngine* SE;
public:
/** Attach timeout to HttpServerSocket
*/
HttpServerTimeout(HttpServerSocket* sock, SocketEngine* engine);
/** Handle timer tick
*/
void Tick(time_t TIME);
};
/** A socket used for HTTP transport
*/
class HttpServerSocket : public InspSocket
{
FileReader* index;
HttpState InternalState;
std::stringstream headers;
std::string postdata;
std::string request_type;
std::string uri;
std::string http_version;
unsigned int postsize;
HttpServerTimeout* Timeout;
public:
HttpServerSocket(InspIRCd* SI, std::string host, int port, bool listening, unsigned long maxtime, FileReader* index_page) : InspSocket(SI, host, port, listening, maxtime), index(index_page), postsize(0)
{
InternalState = HTTP_LISTEN;
Timeout = NULL;
}
HttpServerSocket(InspIRCd* SI, int newfd, char* ip, FileReader* ind) : InspSocket(SI, newfd, ip), index(ind), postsize(0)
{
InternalState = HTTP_SERVE_WAIT_REQUEST;
Timeout = new HttpServerTimeout(this, Instance->SE);
Instance->Timers->AddTimer(Timeout);
}
FileReader* GetIndex()
{
return index;
}
~HttpServerSocket()
{
if (Timeout)
{
if (Instance->Time() < Timeout->GetTimer())
Instance->Timers->DelTimer(Timeout);
Timeout = NULL;
}
}
virtual int OnIncomingConnection(int newsock, char* ip)
{
if (InternalState == HTTP_LISTEN)
{
HttpServerSocket* s = new HttpServerSocket(this->Instance, newsock, ip, index);
s = s; /* Stop GCC whining */
}
return true;
}
virtual void OnClose()
{
}
std::string Response(int response)
{
switch (response)
{
case 100:
return "CONTINUE";
case 101:
return "SWITCHING PROTOCOLS";
case 200:
return "OK";
case 201:
return "CREATED";
case 202:
return "ACCEPTED";
case 203:
return "NON-AUTHORITATIVE INFORMATION";
case 204:
return "NO CONTENT";
case 205:
return "RESET CONTENT";
case 206:
return "PARTIAL CONTENT";
case 300:
return "MULTIPLE CHOICES";
case 301:
return "MOVED PERMENANTLY";
case 302:
return "FOUND";
case 303:
return "SEE OTHER";
case 304:
return "NOT MODIFIED";
case 305:
return "USE PROXY";
case 307:
return "TEMPORARY REDIRECT";
case 400:
return "BAD REQUEST";
case 401:
return "UNAUTHORIZED";
case 402:
return "PAYMENT REQUIRED";
case 403:
return "FORBIDDEN";
case 404:
return "NOT FOUND";
case 405:
return "METHOD NOT ALLOWED";
case 406:
return "NOT ACCEPTABLE";
case 407:
return "PROXY AUTHENTICATION REQUIRED";
case 408:
return "REQUEST TIMEOUT";
case 409:
return "CONFLICT";
case 410:
return "GONE";
case 411:
return "LENGTH REQUIRED";
case 412:
return "PRECONDITION FAILED";
case 413:
return "REQUEST ENTITY TOO LARGE";
case 414:
return "REQUEST-URI TOO LONG";
case 415:
return "UNSUPPORTED MEDIA TYPE";
case 416:
return "REQUESTED RANGE NOT SATISFIABLE";
case 417:
return "EXPECTATION FAILED";
case 500:
return "INTERNAL SERVER ERROR";
case 501:
return "NOT IMPLEMENTED";
case 502:
return "BAD GATEWAY";
case 503:
return "SERVICE UNAVAILABLE";
case 504:
return "GATEWAY TIMEOUT";
case 505:
return "HTTP VERSION NOT SUPPORTED";
default:
return "WTF";
break;
}
}
void SendHeaders(unsigned long size, int response, const std::string &extraheaders)
{
time_t local = this->Instance->Time();
struct tm *timeinfo = gmtime(&local);
this->Write("HTTP/1.1 "+ConvToStr(response)+" "+Response(response)+"\r\nDate: ");
this->Write(asctime(timeinfo));
if (extraheaders.empty())
{
this->Write("Content-Type: text/html\r\n");
}
else
{
this->Write(extraheaders);
}
this->Write("Server: InspIRCd/m_httpd.so/1.1\r\nContent-Length: "+ConvToStr(size)+
"\r\nConnection: close\r\n\r\n");
}
virtual bool OnDataReady()
{
char* data = this->Read();
/* Check that the data read is a valid pointer and it has some content */
if (data && *data)
{
headers << data;
if (headers.str().find("\r\n\r\n") != std::string::npos)
{
if (request_type.empty())
{
headers >> request_type;
headers >> uri;
headers >> http_version;
std::transform(request_type.begin(), request_type.end(), request_type.begin(), ::toupper);
std::transform(http_version.begin(), http_version.end(), http_version.begin(), ::toupper);
}
if ((InternalState == HTTP_SERVE_WAIT_REQUEST) && (request_type == "POST"))
{
/* Do we need to fetch postdata? */
postdata.clear();
InternalState = HTTP_SERVE_RECV_POSTDATA;
std::string header_item;
while (headers >> header_item)
{
if (header_item == "Content-Length:")
{
headers >> header_item;
postsize = atoi(header_item.c_str());
}
}
if (!postsize)
{
InternalState = HTTP_SERVE_SEND_DATA;
SendHeaders(0, 400, "");
Timeout = new HttpServerTimeout(this, Instance->SE);
Instance->Timers->AddTimer(Timeout);
}
else
{
std::string::size_type x = headers.str().find("\r\n\r\n");
postdata = headers.str().substr(x+4, headers.str().length());
/* Get content length and store */
if (postdata.length() >= postsize)
ServeData();
}
}
else if (InternalState == HTTP_SERVE_RECV_POSTDATA)
{
/* Add postdata, once we have it all, send the event */
postdata.append(data);
if (postdata.length() >= postsize)
ServeData();
}
else
{
ServeData();
}
return true;
}
return true;
}
else
{
return false;
}
}
void ServeData()
{
/* Headers are complete */
InternalState = HTTP_SERVE_SEND_DATA;
Instance->Timers->DelTimer(Timeout);
Timeout = NULL;
if ((http_version != "HTTP/1.1") && (http_version != "HTTP/1.0"))
{
SendHeaders(0, 505, "");
}
else
{
if ((request_type == "GET") && (uri == "/"))
{
SendHeaders(index->ContentSize(), 200, "");
this->Write(index->Contents());
}
else
{
claimed = false;
HTTPRequest httpr(request_type,uri,&headers,this,this->GetIP(),postdata);
Event e((char*)&httpr, (Module*)HttpModule, "httpd_url");
e.Send(this->Instance);
if (!claimed)
{
SendHeaders(0, 404, "");
}
}
}
Timeout = new HttpServerTimeout(this, Instance->SE);
Instance->Timers->AddTimer(Timeout);
}
void Page(std::stringstream* n, int response, std::string& extraheaders)
{
SendHeaders(n->str().length(), response, extraheaders);
this->Write(n->str());
}
};
HttpServerTimeout::HttpServerTimeout(HttpServerSocket* sock, SocketEngine* engine) : InspTimer(60, time(NULL)), s(sock), SE(engine)
{
}
void HttpServerTimeout::Tick(time_t TIME)
{
SE->DelFd(s);
s->Close();
}
class ModuleHttpServer : public Module
{
std::vector<HttpServerSocket*> httpsocks;
public:
void ReadConfig()
{
int port;
std::string host;
std::string bindip;
std::string indexfile;
FileReader* index;
HttpServerSocket* http;
ConfigReader c(ServerInstance);
httpsocks.clear();
for (int i = 0; i < c.Enumerate("http"); i++)
{
host = c.ReadValue("http", "host", i);
bindip = c.ReadValue("http", "ip", i);
port = c.ReadInteger("http", "port", i, true);
indexfile = c.ReadValue("http", "index", i);
index = new FileReader(ServerInstance, indexfile);
if (!index->Exists())
throw ModuleException("Can't read index file: "+indexfile);
http = new HttpServerSocket(ServerInstance, bindip, port, true, 0, index);
httpsocks.push_back(http);
}
}
ModuleHttpServer(InspIRCd* Me) : Module(Me)
{
ReadConfig();
}
void OnEvent(Event* event)
{
}
char* OnRequest(Request* request)
{
claimed = true;
HTTPDocument* doc = (HTTPDocument*)request->GetData();
HttpServerSocket* sock = (HttpServerSocket*)doc->sock;
sock->Page(doc->GetDocument(), doc->GetResponseCode(), doc->GetExtraHeaders());
return NULL;
}
void Implements(char* List)
{
List[I_OnEvent] = List[I_OnRequest] = 1;
}
virtual ~ModuleHttpServer()
{
for (size_t i = 0; i < httpsocks.size(); i++)
{
ServerInstance->SE->DelFd(httpsocks[i]);
delete httpsocks[i]->GetIndex();
delete httpsocks[i];
}
}
virtual Version GetVersion()
{
return Version(1,1,0,0,VF_VENDOR|VF_SERVICEPROVIDER,API_VERSION);
}
};
MODULE_INIT(ModuleHttpServer)
\ No newline at end of file +/* +------------------------------------+ + * | 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 <algorithm> +#include "modules.h" +#include "httpd.h" + +/* $ModDesc: Provides HTTP serving facilities to modules */ + +class ModuleHttpServer; + +static ModuleHttpServer* HttpModule; +static bool claimed; + +/** HTTP socket states + */ +enum HttpState +{ + HTTP_LISTEN = 0, + HTTP_SERVE_WAIT_REQUEST = 1, + HTTP_SERVE_RECV_POSTDATA = 2, + HTTP_SERVE_SEND_DATA = 3 +}; + +class HttpServerSocket; + +/** This class is used to handle HTTP socket timeouts + */ +class HttpServerTimeout : public InspTimer +{ + private: + /** HttpServerSocket we are attached to + */ + HttpServerSocket* s; + /** Socketengine the file descriptor is in + */ + SocketEngine* SE; + public: + /** Attach timeout to HttpServerSocket + */ + HttpServerTimeout(HttpServerSocket* sock, SocketEngine* engine); + /** Handle timer tick + */ + void Tick(time_t TIME); +}; + +/** A socket used for HTTP transport + */ +class HttpServerSocket : public InspSocket +{ + FileReader* index; + HttpState InternalState; + std::stringstream headers; + std::string postdata; + std::string request_type; + std::string uri; + std::string http_version; + unsigned int postsize; + HttpServerTimeout* Timeout; + + public: + + HttpServerSocket(InspIRCd* SI, std::string host, int port, bool listening, unsigned long maxtime, FileReader* index_page) : InspSocket(SI, host, port, listening, maxtime), index(index_page), postsize(0) + { + InternalState = HTTP_LISTEN; + Timeout = NULL; + } + + HttpServerSocket(InspIRCd* SI, int newfd, char* ip, FileReader* ind) : InspSocket(SI, newfd, ip), index(ind), postsize(0) + { + InternalState = HTTP_SERVE_WAIT_REQUEST; + Timeout = new HttpServerTimeout(this, Instance->SE); + Instance->Timers->AddTimer(Timeout); + } + + FileReader* GetIndex() + { + return index; + } + + ~HttpServerSocket() + { + if (Timeout) + { + if (Instance->Time() < Timeout->GetTimer()) + Instance->Timers->DelTimer(Timeout); + Timeout = NULL; + } + } + + virtual int OnIncomingConnection(int newsock, char* ip) + { + if (InternalState == HTTP_LISTEN) + { + HttpServerSocket* s = new HttpServerSocket(this->Instance, newsock, ip, index); + s = s; /* Stop GCC whining */ + } + return true; + } + + virtual void OnClose() + { + } + + std::string Response(int response) + { + switch (response) + { + case 100: + return "CONTINUE"; + case 101: + return "SWITCHING PROTOCOLS"; + case 200: + return "OK"; + case 201: + return "CREATED"; + case 202: + return "ACCEPTED"; + case 203: + return "NON-AUTHORITATIVE INFORMATION"; + case 204: + return "NO CONTENT"; + case 205: + return "RESET CONTENT"; + case 206: + return "PARTIAL CONTENT"; + case 300: + return "MULTIPLE CHOICES"; + case 301: + return "MOVED PERMENANTLY"; + case 302: + return "FOUND"; + case 303: + return "SEE OTHER"; + case 304: + return "NOT MODIFIED"; + case 305: + return "USE PROXY"; + case 307: + return "TEMPORARY REDIRECT"; + case 400: + return "BAD REQUEST"; + case 401: + return "UNAUTHORIZED"; + case 402: + return "PAYMENT REQUIRED"; + case 403: + return "FORBIDDEN"; + case 404: + return "NOT FOUND"; + case 405: + return "METHOD NOT ALLOWED"; + case 406: + return "NOT ACCEPTABLE"; + case 407: + return "PROXY AUTHENTICATION REQUIRED"; + case 408: + return "REQUEST TIMEOUT"; + case 409: + return "CONFLICT"; + case 410: + return "GONE"; + case 411: + return "LENGTH REQUIRED"; + case 412: + return "PRECONDITION FAILED"; + case 413: + return "REQUEST ENTITY TOO LARGE"; + case 414: + return "REQUEST-URI TOO LONG"; + case 415: + return "UNSUPPORTED MEDIA TYPE"; + case 416: + return "REQUESTED RANGE NOT SATISFIABLE"; + case 417: + return "EXPECTATION FAILED"; + case 500: + return "INTERNAL SERVER ERROR"; + case 501: + return "NOT IMPLEMENTED"; + case 502: + return "BAD GATEWAY"; + case 503: + return "SERVICE UNAVAILABLE"; + case 504: + return "GATEWAY TIMEOUT"; + case 505: + return "HTTP VERSION NOT SUPPORTED"; + default: + return "WTF"; + break; + + } + } + + void SendHeaders(unsigned long size, int response, const std::string &extraheaders) + { + time_t local = this->Instance->Time(); + struct tm *timeinfo = gmtime(&local); + this->Write("HTTP/1.1 "+ConvToStr(response)+" "+Response(response)+"\r\nDate: "); + this->Write(asctime(timeinfo)); + if (extraheaders.empty()) + { + this->Write("Content-Type: text/html\r\n"); + } + else + { + this->Write(extraheaders); + } + this->Write("Server: InspIRCd/m_httpd.so/1.1\r\nContent-Length: "+ConvToStr(size)+ + "\r\nConnection: close\r\n\r\n"); + } + + virtual bool OnDataReady() + { + char* data = this->Read(); + + /* Check that the data read is a valid pointer and it has some content */ + if (data && *data) + { + headers << data; + + if (headers.str().find("\r\n\r\n") != std::string::npos) + { + if (request_type.empty()) + { + headers >> request_type; + headers >> uri; + headers >> http_version; + + std::transform(request_type.begin(), request_type.end(), request_type.begin(), ::toupper); + std::transform(http_version.begin(), http_version.end(), http_version.begin(), ::toupper); + } + + if ((InternalState == HTTP_SERVE_WAIT_REQUEST) && (request_type == "POST")) + { + /* Do we need to fetch postdata? */ + postdata.clear(); + InternalState = HTTP_SERVE_RECV_POSTDATA; + std::string header_item; + while (headers >> header_item) + { + if (header_item == "Content-Length:") + { + headers >> header_item; + postsize = atoi(header_item.c_str()); + } + } + if (!postsize) + { + InternalState = HTTP_SERVE_SEND_DATA; + SendHeaders(0, 400, ""); + Timeout = new HttpServerTimeout(this, Instance->SE); + Instance->Timers->AddTimer(Timeout); + } + else + { + std::string::size_type x = headers.str().find("\r\n\r\n"); + postdata = headers.str().substr(x+4, headers.str().length()); + /* Get content length and store */ + if (postdata.length() >= postsize) + ServeData(); + } + } + else if (InternalState == HTTP_SERVE_RECV_POSTDATA) + { + /* Add postdata, once we have it all, send the event */ + postdata.append(data); + if (postdata.length() >= postsize) + ServeData(); + } + else + { + ServeData(); + } + return true; + } + return true; + } + else + { + return false; + } + } + + void ServeData() + { + /* Headers are complete */ + InternalState = HTTP_SERVE_SEND_DATA; + + Instance->Timers->DelTimer(Timeout); + Timeout = NULL; + + if ((http_version != "HTTP/1.1") && (http_version != "HTTP/1.0")) + { + SendHeaders(0, 505, ""); + } + else + { + if ((request_type == "GET") && (uri == "/")) + { + SendHeaders(index->ContentSize(), 200, ""); + this->Write(index->Contents()); + } + else + { + claimed = false; + HTTPRequest httpr(request_type,uri,&headers,this,this->GetIP(),postdata); + Event e((char*)&httpr, (Module*)HttpModule, "httpd_url"); + e.Send(this->Instance); + if (!claimed) + { + SendHeaders(0, 404, ""); + } + } + } + Timeout = new HttpServerTimeout(this, Instance->SE); + Instance->Timers->AddTimer(Timeout); + } + + void Page(std::stringstream* n, int response, std::string& extraheaders) + { + SendHeaders(n->str().length(), response, extraheaders); + this->Write(n->str()); + } +}; + +HttpServerTimeout::HttpServerTimeout(HttpServerSocket* sock, SocketEngine* engine) : InspTimer(60, time(NULL)), s(sock), SE(engine) +{ +} + +void HttpServerTimeout::Tick(time_t TIME) +{ + SE->DelFd(s); + s->Close(); +} + +class ModuleHttpServer : public Module +{ + std::vector<HttpServerSocket*> httpsocks; + public: + + void ReadConfig() + { + int port; + std::string host; + std::string bindip; + std::string indexfile; + FileReader* index; + HttpServerSocket* http; + ConfigReader c(ServerInstance); + + httpsocks.clear(); + + for (int i = 0; i < c.Enumerate("http"); i++) + { + host = c.ReadValue("http", "host", i); + bindip = c.ReadValue("http", "ip", i); + port = c.ReadInteger("http", "port", i, true); + indexfile = c.ReadValue("http", "index", i); + index = new FileReader(ServerInstance, indexfile); + if (!index->Exists()) + throw ModuleException("Can't read index file: "+indexfile); + http = new HttpServerSocket(ServerInstance, bindip, port, true, 0, index); + httpsocks.push_back(http); + } + } + + ModuleHttpServer(InspIRCd* Me) : Module(Me) + { + ReadConfig(); + } + + void OnEvent(Event* event) + { + } + + char* OnRequest(Request* request) + { + claimed = true; + HTTPDocument* doc = (HTTPDocument*)request->GetData(); + HttpServerSocket* sock = (HttpServerSocket*)doc->sock; + sock->Page(doc->GetDocument(), doc->GetResponseCode(), doc->GetExtraHeaders()); + return NULL; + } + + void Implements(char* List) + { + List[I_OnEvent] = List[I_OnRequest] = 1; + } + + virtual ~ModuleHttpServer() + { + for (size_t i = 0; i < httpsocks.size(); i++) + { + ServerInstance->SE->DelFd(httpsocks[i]); + delete httpsocks[i]->GetIndex(); + delete httpsocks[i]; + } + } + + virtual Version GetVersion() + { + return Version(1,1,0,0,VF_VENDOR|VF_SERVICEPROVIDER,API_VERSION); + } +}; + +MODULE_INIT(ModuleHttpServer) diff --git a/src/modules/m_httpd_stats.cpp b/src/modules/m_httpd_stats.cpp index 49b5bbab5..5c29123f8 100644 --- a/src/modules/m_httpd_stats.cpp +++ b/src/modules/m_httpd_stats.cpp @@ -1 +1,241 @@ -/* +------------------------------------+
* | 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 "users.h"
#include "channels.h"
#include "configreader.h"
#include "modules.h"
#include "inspsocket.h"
#include "httpd.h"
/* $ModDesc: Provides statistics over HTTP via m_httpd.so */
typedef std::map<irc::string,int> StatsHash;
typedef StatsHash::iterator StatsIter;
typedef std::vector<std::pair<int,irc::string> > SortedList;
typedef SortedList::iterator SortedIter;
static StatsHash* sh = new StatsHash();
static SortedList* so = new SortedList();
class ModuleHttpStats : public Module
{
std::string stylesheet;
bool changed;
public:
void ReadConfig()
{
ConfigReader c(ServerInstance);
this->stylesheet = c.ReadValue("httpstats", "stylesheet", 0);
}
ModuleHttpStats(InspIRCd* Me) : Module(Me)
{
ReadConfig();
this->changed = false;
}
void InsertOrder(irc::string channel, int count)
{
/* This function figures out where in the sorted list to put an item from the hash */
SortedIter a;
for (a = so->begin(); a != so->end(); a++)
{
/* Found an item equal to or less than, we insert our item before it */
if (a->first <= count)
{
so->insert(a,std::pair<int,irc::string>(count,channel));
return;
}
}
/* There are no items in the list yet, insert something at the beginning */
so->insert(so->begin(), std::pair<int,irc::string>(count,channel));
}
void SortList()
{
/* Sorts the hash into the sorted list using an insertion sort */
so->clear();
for (StatsIter a = sh->begin(); a != sh->end(); a++)
InsertOrder(a->first, a->second);
this->changed = false;
}
void OnEvent(Event* event)
{
std::stringstream data("");
if (event->GetEventID() == "httpd_url")
{
HTTPRequest* http = (HTTPRequest*)event->GetData();
if ((http->GetURI() == "/stats") || (http->GetURI() == "/stats/"))
{
data << "<!DOCTYPE html PUBLIC \
\"-//W3C//DTD XHTML 1.1//EN\" \
\"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd\">\n\
<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\">";
data << "<head>";
data << "<link rel='stylesheet' href='" << this->stylesheet << "' type='text/css' />";
data << "<title>InspIRCd server statisitics for " << ServerInstance->Config->ServerName << " (" << ServerInstance->Config->ServerDesc << ")</title>";
data << "</head><body>";
data << "<h1>InspIRCd server statisitics for " << ServerInstance->Config->ServerName << " (" << ServerInstance->Config->ServerDesc << ")</h1>";
data << "<div class='totals'>";
data << "<h2>Totals</h2>";
data << "<table>";
data << "<tr><td>Users</td><td>" << ServerInstance->clientlist->size() << "</td></tr>";
data << "<tr><td>Channels</td><td>" << ServerInstance->chanlist->size() << "</td></tr>";
data << "<tr><td>Opers</td><td>" << ServerInstance->all_opers.size() << "</td></tr>";
data << "<tr><td>Sockets</td><td>" << (ServerInstance->SE->GetMaxFds() - ServerInstance->SE->GetRemainingFds()) << " (Max: " << ServerInstance->SE->GetMaxFds() << " via socket engine '" << ServerInstance->SE->GetName() << "')</td></tr>";
data << "</table>";
data << "</div>";
data << "<div class='modules'>";
data << "<h2>Modules</h2>";
data << "<table>";
for (int i = 0; i <= ServerInstance->GetModuleCount(); i++)
{
if (!ServerInstance->Config->module_names[i].empty())
data << "<tr><td>" << ServerInstance->Config->module_names[i] << "</td></tr>";
}
data << "</table>";
data << "</div>";
data << "<div class='channels'>";
data << "<h2>Channels</h2>";
data << "<table>";
data << "<tr><th>Users</th><th>Name</th><th>@</th><th>%</th><th>+</th><th>Topic</th></tr>";
/* If the list has changed since last time it was displayed, re-sort it
* this time only (not every time, as this would be moronic)
*/
if (this->changed)
this->SortList();
int n = 0;
for (SortedIter a = so->begin(); ((a != so->end()) && (n < 25)); a++, n++)
{
chanrec* c = ServerInstance->FindChan(a->second.c_str());
if (c)
{
data << "<tr><td>" << a->first << "</td><td>" << a->second << "</td>";
data << "<td>" << c->GetOppedUsers()->size() << "</td>";
data << "<td>" << c->GetHalfoppedUsers()->size() << "</td>";
data << "<td>" << c->GetVoicedUsers()->size() << "</td>";
data << "<td>" << c->topic << "</td>";
data << "</tr>";
}
}
data << "</table>";
data << "</div>";
data << "<div class='validion'>";
data << "<p><a href='http://validator.w3.org/check?uri=referer'><img src='http://www.w3.org/Icons/valid-xhtml11' alt='Valid XHTML 1.1' height='31' width='88' /></a></p>";
data << "</div>";
data << "</body>";
data << "</html>";
/* Send the document back to m_httpd */
HTTPDocument response(http->sock, &data, 200, "X-Powered-By: m_http_stats.so\r\nContent-Type: text/html; charset=iso-8859-1\r\n");
Request req((char*)&response, (Module*)this, event->GetSource());
req.Send();
}
}
}
void OnChannelDelete(chanrec* chan)
{
StatsIter a = sh->find(chan->name);
if (a != sh->end())
{
sh->erase(a);
}
this->changed = true;
}
void OnUserJoin(userrec* user, chanrec* channel, bool &silent)
{
StatsIter a = sh->find(channel->name);
if (a != sh->end())
{
a->second++;
}
else
{
irc::string name = channel->name;
sh->insert(std::pair<irc::string,int>(name,1));
}
this->changed = true;
}
void OnUserPart(userrec* user, chanrec* channel, const std::string &partmessage, bool &silent)
{
StatsIter a = sh->find(channel->name);
if (a != sh->end())
{
a->second--;
}
this->changed = true;
}
void OnUserQuit(userrec* user, const std::string &message, const std::string &oper_message)
{
for (UCListIter v = user->chans.begin(); v != user->chans.end(); v++)
{
chanrec* c = v->first;
StatsIter a = sh->find(c->name);
if (a != sh->end())
{
a->second--;
}
}
this->changed = true;
}
char* OnRequest(Request* request)
{
return NULL;
}
void Implements(char* List)
{
List[I_OnEvent] = List[I_OnRequest] = List[I_OnChannelDelete] = List[I_OnUserJoin] = List[I_OnUserPart] = List[I_OnUserQuit] = 1;
}
virtual ~ModuleHttpStats()
{
delete sh;
delete so;
}
virtual Version GetVersion()
{
return Version(1, 1, 0, 0, VF_VENDOR, API_VERSION);
}
};
MODULE_INIT(ModuleHttpStats)
\ No newline at end of file +/* +------------------------------------+ + * | 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 "users.h" +#include "channels.h" +#include "configreader.h" +#include "modules.h" +#include "inspsocket.h" +#include "httpd.h" + +/* $ModDesc: Provides statistics over HTTP via m_httpd.so */ + +typedef std::map<irc::string,int> StatsHash; +typedef StatsHash::iterator StatsIter; + +typedef std::vector<std::pair<int,irc::string> > SortedList; +typedef SortedList::iterator SortedIter; + +static StatsHash* sh = new StatsHash(); +static SortedList* so = new SortedList(); + +class ModuleHttpStats : public Module +{ + + std::string stylesheet; + bool changed; + + public: + + void ReadConfig() + { + ConfigReader c(ServerInstance); + this->stylesheet = c.ReadValue("httpstats", "stylesheet", 0); + } + + ModuleHttpStats(InspIRCd* Me) : Module(Me) + { + + ReadConfig(); + this->changed = false; + } + + void InsertOrder(irc::string channel, int count) + { + /* This function figures out where in the sorted list to put an item from the hash */ + SortedIter a; + for (a = so->begin(); a != so->end(); a++) + { + /* Found an item equal to or less than, we insert our item before it */ + if (a->first <= count) + { + so->insert(a,std::pair<int,irc::string>(count,channel)); + return; + } + } + /* There are no items in the list yet, insert something at the beginning */ + so->insert(so->begin(), std::pair<int,irc::string>(count,channel)); + } + + void SortList() + { + /* Sorts the hash into the sorted list using an insertion sort */ + so->clear(); + for (StatsIter a = sh->begin(); a != sh->end(); a++) + InsertOrder(a->first, a->second); + this->changed = false; + } + + void OnEvent(Event* event) + { + std::stringstream data(""); + + if (event->GetEventID() == "httpd_url") + { + HTTPRequest* http = (HTTPRequest*)event->GetData(); + + if ((http->GetURI() == "/stats") || (http->GetURI() == "/stats/")) + { + data << "<!DOCTYPE html PUBLIC \ + \"-//W3C//DTD XHTML 1.1//EN\" \ + \"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd\">\n\ + <html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\">"; + + data << "<head>"; + data << "<link rel='stylesheet' href='" << this->stylesheet << "' type='text/css' />"; + data << "<title>InspIRCd server statisitics for " << ServerInstance->Config->ServerName << " (" << ServerInstance->Config->ServerDesc << ")</title>"; + data << "</head><body>"; + data << "<h1>InspIRCd server statisitics for " << ServerInstance->Config->ServerName << " (" << ServerInstance->Config->ServerDesc << ")</h1>"; + + data << "<div class='totals'>"; + data << "<h2>Totals</h2>"; + data << "<table>"; + data << "<tr><td>Users</td><td>" << ServerInstance->clientlist->size() << "</td></tr>"; + data << "<tr><td>Channels</td><td>" << ServerInstance->chanlist->size() << "</td></tr>"; + data << "<tr><td>Opers</td><td>" << ServerInstance->all_opers.size() << "</td></tr>"; + data << "<tr><td>Sockets</td><td>" << (ServerInstance->SE->GetMaxFds() - ServerInstance->SE->GetRemainingFds()) << " (Max: " << ServerInstance->SE->GetMaxFds() << " via socket engine '" << ServerInstance->SE->GetName() << "')</td></tr>"; + data << "</table>"; + data << "</div>"; + + data << "<div class='modules'>"; + data << "<h2>Modules</h2>"; + data << "<table>"; + for (int i = 0; i <= ServerInstance->GetModuleCount(); i++) + { + if (!ServerInstance->Config->module_names[i].empty()) + data << "<tr><td>" << ServerInstance->Config->module_names[i] << "</td></tr>"; + } + data << "</table>"; + data << "</div>"; + + data << "<div class='channels'>"; + data << "<h2>Channels</h2>"; + data << "<table>"; + data << "<tr><th>Users</th><th>Name</th><th>@</th><th>%</th><th>+</th><th>Topic</th></tr>"; + + /* If the list has changed since last time it was displayed, re-sort it + * this time only (not every time, as this would be moronic) + */ + if (this->changed) + this->SortList(); + + int n = 0; + for (SortedIter a = so->begin(); ((a != so->end()) && (n < 25)); a++, n++) + { + chanrec* c = ServerInstance->FindChan(a->second.c_str()); + if (c) + { + data << "<tr><td>" << a->first << "</td><td>" << a->second << "</td>"; + data << "<td>" << c->GetOppedUsers()->size() << "</td>"; + data << "<td>" << c->GetHalfoppedUsers()->size() << "</td>"; + data << "<td>" << c->GetVoicedUsers()->size() << "</td>"; + data << "<td>" << c->topic << "</td>"; + data << "</tr>"; + } + } + + data << "</table>"; + data << "</div>"; + + + + + + data << "<div class='validion'>"; + data << "<p><a href='http://validator.w3.org/check?uri=referer'><img src='http://www.w3.org/Icons/valid-xhtml11' alt='Valid XHTML 1.1' height='31' width='88' /></a></p>"; + data << "</div>"; + + data << "</body>"; + data << "</html>"; + + /* Send the document back to m_httpd */ + HTTPDocument response(http->sock, &data, 200, "X-Powered-By: m_http_stats.so\r\nContent-Type: text/html; charset=iso-8859-1\r\n"); + Request req((char*)&response, (Module*)this, event->GetSource()); + req.Send(); + } + } + } + + void OnChannelDelete(chanrec* chan) + { + StatsIter a = sh->find(chan->name); + if (a != sh->end()) + { + sh->erase(a); + } + this->changed = true; + } + + void OnUserJoin(userrec* user, chanrec* channel, bool &silent) + { + StatsIter a = sh->find(channel->name); + if (a != sh->end()) + { + a->second++; + } + else + { + irc::string name = channel->name; + sh->insert(std::pair<irc::string,int>(name,1)); + } + this->changed = true; + } + + void OnUserPart(userrec* user, chanrec* channel, const std::string &partmessage, bool &silent) + { + StatsIter a = sh->find(channel->name); + if (a != sh->end()) + { + a->second--; + } + this->changed = true; + } + + void OnUserQuit(userrec* user, const std::string &message, const std::string &oper_message) + { + for (UCListIter v = user->chans.begin(); v != user->chans.end(); v++) + { + chanrec* c = v->first; + StatsIter a = sh->find(c->name); + if (a != sh->end()) + { + a->second--; + } + } + this->changed = true; + } + + char* OnRequest(Request* request) + { + return NULL; + } + + void Implements(char* List) + { + List[I_OnEvent] = List[I_OnRequest] = List[I_OnChannelDelete] = List[I_OnUserJoin] = List[I_OnUserPart] = List[I_OnUserQuit] = 1; + } + + virtual ~ModuleHttpStats() + { + delete sh; + delete so; + } + + virtual Version GetVersion() + { + return Version(1, 1, 0, 0, VF_VENDOR, API_VERSION); + } +}; + +MODULE_INIT(ModuleHttpStats) diff --git a/src/modules/m_ident.cpp b/src/modules/m_ident.cpp index bf71f8189..732c2eaee 100644 --- a/src/modules/m_ident.cpp +++ b/src/modules/m_ident.cpp @@ -1 +1,326 @@ -/* +------------------------------------+
* | 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 "users.h"
#include "channels.h"
#include "modules.h"
/* $ModDesc: Provides support for RFC 1413 ident lookups */
// Version 1.5.0.0 - Updated to use InspSocket, faster and neater.
/** Handles RFC1413 ident connections to users
*/
class RFC1413 : public InspSocket
{
protected:
socklen_t uslen; // length of our port number
socklen_t themlen; // length of their port number
char ident_request[128]; // buffer used to make up the request string
public:
userrec* u; // user record that the lookup is associated with
int ufd;
RFC1413(InspIRCd* SI, userrec* user, int maxtime, const std::string &bindto) : InspSocket(SI, user->GetIPString(), 113, false, maxtime, bindto), u(user)
{
ufd = user->GetFd();
}
virtual void OnTimeout()
{
// When we timeout, the connection failed within the allowed timeframe,
// so we just display a notice, and tidy off the ident_data.
if (u && (Instance->SE->GetRef(ufd) == u))
{
u->Shrink("ident_data");
Instance->next_call = Instance->Time();
}
}
virtual bool OnDataReady()
{
char* ibuf = this->Read();
if (ibuf)
{
char* savept;
char* section = strtok_r(ibuf,":",&savept);
while (section)
{
if (strstr(section,"USERID"))
{
section = strtok_r(NULL,":",&savept);
if (section)
{
// ID type, usually UNIX or OTHER... we dont want it, so read the next token
section = strtok_r(NULL,":",&savept);
if (section)
{
while (*section == ' ') section++; // strip leading spaces
for (char* j = section; *j; j++)
if ((*j < 33) || (*j > 126))
*j = '\0'; // truncate at invalid chars
if (*section)
{
if (u && (Instance->SE->GetRef(ufd) == u))
{
if (this->Instance->IsIdent(section))
{
u->Extend("IDENT", new std::string(std::string(section) + "," + std::string(u->ident)));
strlcpy(u->ident,section,IDENTMAX);
u->WriteServ("NOTICE "+std::string(u->nick)+" :*** Found your ident: "+std::string(u->ident));
}
}
}
return false;
}
}
}
section = strtok_r(NULL,":",&savept);
}
}
return false;
}
virtual void OnClose()
{
// tidy up after ourselves when the connection is done.
// We receive this event straight after a timeout, too.
//
//
// OK, now listen up. The weird looking check here is
// REQUIRED. Don't try and optimize it away.
//
// When a socket is closed, it is not immediately removed
// from the socket list, there can be a short delay
// before it is culled from the list. This means that
// without this check, there is a chance that a user
// may not exist when we come to ::Shrink them, which
// results in a segfault. The value of "u" may not
// always be NULL at this point, so, what we do is
// check against the fd_ref_table, to see if (1) the user
// exists, and (2) its the SAME user, on the same file
// descriptor that they were when the lookup began.
//
// Fixes issue reported by webs, 7 Jun 2006
if (u && (Instance->SE->GetRef(ufd) == u))
{
Instance->next_call = Instance->Time();
u->Shrink("ident_data");
}
}
virtual void OnError(InspSocketError e)
{
if (u && (Instance->SE->GetRef(ufd) == u))
{
if (*u->ident == '~')
u->WriteServ("NOTICE "+std::string(u->nick)+" :*** Could not find your ident, using "+std::string(u->ident)+" instead.");
Instance->next_call = Instance->Time();
u->Shrink("ident_data");
}
}
virtual bool OnConnected()
{
if (u && (Instance->SE->GetRef(ufd) == u))
{
sockaddr* sock_us = new sockaddr[2];
sockaddr* sock_them = new sockaddr[2];
bool success = false;
uslen = sizeof(sockaddr_in);
themlen = sizeof(sockaddr_in);
#ifdef IPV6
if (this->u->GetProtocolFamily() == AF_INET6)
{
themlen = sizeof(sockaddr_in6);
uslen = sizeof(sockaddr_in6);
}
#endif
success = ((getsockname(this->u->GetFd(),sock_us,&uslen) || getpeername(this->u->GetFd(), sock_them, &themlen)));
if (success)
{
delete[] sock_us;
delete[] sock_them;
return false;
}
else
{
// send the request in the following format: theirsocket,oursocket
#ifdef IPV6
if (this->u->GetProtocolFamily() == AF_INET6)
snprintf(ident_request,127,"%d,%d\r\n",ntohs(((sockaddr_in6*)sock_them)->sin6_port),ntohs(((sockaddr_in6*)sock_us)->sin6_port));
else
#endif
snprintf(ident_request,127,"%d,%d\r\n",ntohs(((sockaddr_in*)sock_them)->sin_port),ntohs(((sockaddr_in*)sock_us)->sin_port));
this->Write(ident_request);
delete[] sock_us;
delete[] sock_them;
return true;
}
}
else
{
Instance->next_call = Instance->Time();
return true;
}
}
};
class ModuleIdent : public Module
{
ConfigReader* Conf;
int IdentTimeout;
std::string PortBind;
public:
void ReadSettings()
{
Conf = new ConfigReader(ServerInstance);
IdentTimeout = Conf->ReadInteger("ident", "timeout", 0, true);
PortBind = Conf->ReadValue("ident", "bind", 0);
if (!IdentTimeout)
IdentTimeout = 1;
DELETE(Conf);
}
ModuleIdent(InspIRCd* Me)
: Module(Me)
{
ReadSettings();
}
void Implements(char* List)
{
List[I_OnCleanup] = List[I_OnRehash] = List[I_OnUserRegister] = List[I_OnCheckReady] = List[I_OnUserDisconnect] = 1;
}
void OnSyncUserMetaData(userrec* user, Module* proto,void* opaque, const std::string &extname, bool displayable)
{
if ((displayable) && (extname == "IDENT"))
{
std::string* ident;
if (GetExt("IDENT", ident))
proto->ProtoSendMetaData(opaque, TYPE_USER, user, extname, *ident);
}
}
virtual void OnRehash(userrec* user, const std::string ¶meter)
{
ReadSettings();
}
virtual int OnUserRegister(userrec* user)
{
/*
* when the new user connects, before they authenticate with USER/NICK/PASS, we do
* their ident lookup. We do this by instantiating an object of type RFC1413, which
* is derived from InspSocket, and inserting it into the socket engine using the
* Server::AddSocket() call.
*/
char newident[MAXBUF];
strcpy(newident,"~");
strlcat(newident,user->ident,IDENTMAX);
strlcpy(user->ident,newident,IDENTMAX);
user->WriteServ("NOTICE "+std::string(user->nick)+" :*** Looking up your ident...");
RFC1413* ident = new RFC1413(ServerInstance, user, IdentTimeout, PortBind);
if ((ident->GetState() == I_CONNECTING) || (ident->GetState() == I_CONNECTED))
{
user->Extend("ident_data", (char*)ident);
}
else
{
user->WriteServ("NOTICE "+std::string(user->nick)+" :*** Could not find your ident, using "+std::string(user->ident)+" instead.");
ServerInstance->next_call = ServerInstance->Time();
}
return 0;
}
virtual bool OnCheckReady(userrec* user)
{
/*
* The socket engine will clean up their ident request for us when it completes,
* either due to timeout or due to closing, so, we just hold them until they dont
* have an ident field any more.
*/
RFC1413* ident;
return (!user->GetExt("ident_data", ident));
}
virtual void OnCleanup(int target_type, void* item)
{
if (target_type == TYPE_USER)
{
userrec* user = (userrec*)item;
RFC1413* ident;
std::string* identstr;
if (user->GetExt("ident_data", ident))
{
// FIX: If the user record is deleted, the socket wont be removed
// immediately so there is chance of the socket trying to write to
// a user which has now vanished! To prevent this, set ident::u
// to NULL and check it so that we dont write users who have gone away.
ident->u = NULL;
ServerInstance->SE->DelFd(ident);
//delete ident;
}
if (user->GetExt("IDENT", identstr))
{
delete identstr;
}
}
}
virtual void OnUserDisconnect(userrec* user)
{
/*
* when the user quits tidy up any ident lookup they have pending to keep things tidy.
* When we call RemoveSocket, the abstractions tied into the system evnetually work their
* way to RFC1459::OnClose(), which shrinks off the ident_data for us, so we dont need
* to do it here. If we don't tidy this up, there may still be lingering idents for users
* who have quit, as class RFC1459 is only loosely bound to userrec* via a pair of pointers
* and this would leave at least one of the invalid ;)
*/
RFC1413* ident;
std::string* identstr;
if (user->GetExt("ident_data", ident))
{
ident->u = NULL;
ServerInstance->SE->DelFd(ident);
}
if (user->GetExt("IDENT", identstr))
{
delete identstr;
}
}
virtual ~ModuleIdent()
{
ServerInstance->next_call = ServerInstance->Time();
}
virtual Version GetVersion()
{
return Version(1,1,0,0,VF_VENDOR,API_VERSION);
}
};
MODULE_INIT(ModuleIdent)
\ No newline at end of file +/* +------------------------------------+ + * | 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 "users.h" +#include "channels.h" +#include "modules.h" + +/* $ModDesc: Provides support for RFC 1413 ident lookups */ + +// Version 1.5.0.0 - Updated to use InspSocket, faster and neater. + +/** Handles RFC1413 ident connections to users + */ +class RFC1413 : public InspSocket +{ + protected: + socklen_t uslen; // length of our port number + socklen_t themlen; // length of their port number + char ident_request[128]; // buffer used to make up the request string + public: + + userrec* u; // user record that the lookup is associated with + int ufd; + + RFC1413(InspIRCd* SI, userrec* user, int maxtime, const std::string &bindto) : InspSocket(SI, user->GetIPString(), 113, false, maxtime, bindto), u(user) + { + ufd = user->GetFd(); + } + + virtual void OnTimeout() + { + // When we timeout, the connection failed within the allowed timeframe, + // so we just display a notice, and tidy off the ident_data. + if (u && (Instance->SE->GetRef(ufd) == u)) + { + u->Shrink("ident_data"); + Instance->next_call = Instance->Time(); + } + } + + virtual bool OnDataReady() + { + char* ibuf = this->Read(); + if (ibuf) + { + char* savept; + char* section = strtok_r(ibuf,":",&savept); + while (section) + { + if (strstr(section,"USERID")) + { + section = strtok_r(NULL,":",&savept); + if (section) + { + // ID type, usually UNIX or OTHER... we dont want it, so read the next token + section = strtok_r(NULL,":",&savept); + if (section) + { + while (*section == ' ') section++; // strip leading spaces + for (char* j = section; *j; j++) + if ((*j < 33) || (*j > 126)) + *j = '\0'; // truncate at invalid chars + if (*section) + { + if (u && (Instance->SE->GetRef(ufd) == u)) + { + if (this->Instance->IsIdent(section)) + { + u->Extend("IDENT", new std::string(std::string(section) + "," + std::string(u->ident))); + strlcpy(u->ident,section,IDENTMAX); + u->WriteServ("NOTICE "+std::string(u->nick)+" :*** Found your ident: "+std::string(u->ident)); + } + } + } + return false; + } + } + } + section = strtok_r(NULL,":",&savept); + } + } + return false; + } + + virtual void OnClose() + { + // tidy up after ourselves when the connection is done. + // We receive this event straight after a timeout, too. + // + // + // OK, now listen up. The weird looking check here is + // REQUIRED. Don't try and optimize it away. + // + // When a socket is closed, it is not immediately removed + // from the socket list, there can be a short delay + // before it is culled from the list. This means that + // without this check, there is a chance that a user + // may not exist when we come to ::Shrink them, which + // results in a segfault. The value of "u" may not + // always be NULL at this point, so, what we do is + // check against the fd_ref_table, to see if (1) the user + // exists, and (2) its the SAME user, on the same file + // descriptor that they were when the lookup began. + // + // Fixes issue reported by webs, 7 Jun 2006 + if (u && (Instance->SE->GetRef(ufd) == u)) + { + Instance->next_call = Instance->Time(); + u->Shrink("ident_data"); + } + } + + virtual void OnError(InspSocketError e) + { + if (u && (Instance->SE->GetRef(ufd) == u)) + { + if (*u->ident == '~') + u->WriteServ("NOTICE "+std::string(u->nick)+" :*** Could not find your ident, using "+std::string(u->ident)+" instead."); + + Instance->next_call = Instance->Time(); + u->Shrink("ident_data"); + } + } + + virtual bool OnConnected() + { + if (u && (Instance->SE->GetRef(ufd) == u)) + { + sockaddr* sock_us = new sockaddr[2]; + sockaddr* sock_them = new sockaddr[2]; + bool success = false; + uslen = sizeof(sockaddr_in); + themlen = sizeof(sockaddr_in); +#ifdef IPV6 + if (this->u->GetProtocolFamily() == AF_INET6) + { + themlen = sizeof(sockaddr_in6); + uslen = sizeof(sockaddr_in6); + } +#endif + success = ((getsockname(this->u->GetFd(),sock_us,&uslen) || getpeername(this->u->GetFd(), sock_them, &themlen))); + if (success) + { + delete[] sock_us; + delete[] sock_them; + return false; + } + else + { + // send the request in the following format: theirsocket,oursocket +#ifdef IPV6 + if (this->u->GetProtocolFamily() == AF_INET6) + snprintf(ident_request,127,"%d,%d\r\n",ntohs(((sockaddr_in6*)sock_them)->sin6_port),ntohs(((sockaddr_in6*)sock_us)->sin6_port)); + else +#endif + snprintf(ident_request,127,"%d,%d\r\n",ntohs(((sockaddr_in*)sock_them)->sin_port),ntohs(((sockaddr_in*)sock_us)->sin_port)); + this->Write(ident_request); + delete[] sock_us; + delete[] sock_them; + return true; + } + } + else + { + Instance->next_call = Instance->Time(); + return true; + } + } +}; + +class ModuleIdent : public Module +{ + + ConfigReader* Conf; + int IdentTimeout; + std::string PortBind; + + public: + void ReadSettings() + { + Conf = new ConfigReader(ServerInstance); + IdentTimeout = Conf->ReadInteger("ident", "timeout", 0, true); + PortBind = Conf->ReadValue("ident", "bind", 0); + if (!IdentTimeout) + IdentTimeout = 1; + DELETE(Conf); + } + + ModuleIdent(InspIRCd* Me) + : Module(Me) + { + + ReadSettings(); + } + + void Implements(char* List) + { + List[I_OnCleanup] = List[I_OnRehash] = List[I_OnUserRegister] = List[I_OnCheckReady] = List[I_OnUserDisconnect] = 1; + } + + void OnSyncUserMetaData(userrec* user, Module* proto,void* opaque, const std::string &extname, bool displayable) + { + if ((displayable) && (extname == "IDENT")) + { + std::string* ident; + if (GetExt("IDENT", ident)) + proto->ProtoSendMetaData(opaque, TYPE_USER, user, extname, *ident); + } + } + + + virtual void OnRehash(userrec* user, const std::string ¶meter) + { + ReadSettings(); + } + + virtual int OnUserRegister(userrec* user) + { + /* + * when the new user connects, before they authenticate with USER/NICK/PASS, we do + * their ident lookup. We do this by instantiating an object of type RFC1413, which + * is derived from InspSocket, and inserting it into the socket engine using the + * Server::AddSocket() call. + */ + char newident[MAXBUF]; + strcpy(newident,"~"); + strlcat(newident,user->ident,IDENTMAX); + strlcpy(user->ident,newident,IDENTMAX); + + + user->WriteServ("NOTICE "+std::string(user->nick)+" :*** Looking up your ident..."); + RFC1413* ident = new RFC1413(ServerInstance, user, IdentTimeout, PortBind); + if ((ident->GetState() == I_CONNECTING) || (ident->GetState() == I_CONNECTED)) + { + user->Extend("ident_data", (char*)ident); + } + else + { + user->WriteServ("NOTICE "+std::string(user->nick)+" :*** Could not find your ident, using "+std::string(user->ident)+" instead."); + ServerInstance->next_call = ServerInstance->Time(); + } + return 0; + } + + virtual bool OnCheckReady(userrec* user) + { + /* + * The socket engine will clean up their ident request for us when it completes, + * either due to timeout or due to closing, so, we just hold them until they dont + * have an ident field any more. + */ + RFC1413* ident; + return (!user->GetExt("ident_data", ident)); + } + + virtual void OnCleanup(int target_type, void* item) + { + if (target_type == TYPE_USER) + { + userrec* user = (userrec*)item; + RFC1413* ident; + std::string* identstr; + if (user->GetExt("ident_data", ident)) + { + // FIX: If the user record is deleted, the socket wont be removed + // immediately so there is chance of the socket trying to write to + // a user which has now vanished! To prevent this, set ident::u + // to NULL and check it so that we dont write users who have gone away. + ident->u = NULL; + ServerInstance->SE->DelFd(ident); + //delete ident; + } + if (user->GetExt("IDENT", identstr)) + { + delete identstr; + } + } + } + + virtual void OnUserDisconnect(userrec* user) + { + /* + * when the user quits tidy up any ident lookup they have pending to keep things tidy. + * When we call RemoveSocket, the abstractions tied into the system evnetually work their + * way to RFC1459::OnClose(), which shrinks off the ident_data for us, so we dont need + * to do it here. If we don't tidy this up, there may still be lingering idents for users + * who have quit, as class RFC1459 is only loosely bound to userrec* via a pair of pointers + * and this would leave at least one of the invalid ;) + */ + RFC1413* ident; + std::string* identstr; + if (user->GetExt("ident_data", ident)) + { + ident->u = NULL; + ServerInstance->SE->DelFd(ident); + } + if (user->GetExt("IDENT", identstr)) + { + delete identstr; + } + } + + virtual ~ModuleIdent() + { + ServerInstance->next_call = ServerInstance->Time(); + } + + virtual Version GetVersion() + { + return Version(1,1,0,0,VF_VENDOR,API_VERSION); + } + +}; + +MODULE_INIT(ModuleIdent) diff --git a/src/modules/m_invisible.cpp b/src/modules/m_invisible.cpp index ce2f2062b..e1fb88ca0 100644 --- a/src/modules/m_invisible.cpp +++ b/src/modules/m_invisible.cpp @@ -1 +1,277 @@ -/* +------------------------------------+
* | 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 "users.h"
#include "channels.h"
#include "modules.h"
#include <stdarg.h>
/* $ModDesc: Allows for opered clients to join channels without being seen, similar to unreal 3.1 +I mode */
static ConfigReader* conf;
class QuietOper : public VisData
{
public:
QuietOper()
{
}
virtual ~QuietOper()
{
}
virtual bool VisibleTo(userrec* user)
{
return IS_OPER(user);
}
};
class InvisibleMode : public ModeHandler
{
QuietOper* qo;
public:
InvisibleMode(InspIRCd* Instance) : ModeHandler(Instance, 'Q', 0, 0, false, MODETYPE_USER, true)
{
qo = new QuietOper();
}
~InvisibleMode()
{
for (user_hash::iterator i = ServerInstance->clientlist->begin(); i != ServerInstance->clientlist->end(); i++)
if (i->second->Visibility == qo)
i->second->Visibility = NULL;
delete qo;
}
ModeAction OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string ¶meter, bool adding)
{
if (source != dest)
return MODEACTION_DENY;
if (dest->IsModeSet('Q') != adding)
{
bool ok = false;
for (int j = 0; j < conf->Enumerate("type"); j++)
{
std::string opertype = conf->ReadValue("type","name",j);
if (opertype == source->oper)
{
ok = conf->ReadFlag("type", "canquiet", j);
break;
}
}
if (!ok)
{
source->WriteServ("481 %s :Permission Denied - You do not have access to become invisible via user mode +Q", source->nick);
return MODEACTION_DENY;
}
dest->SetMode('Q', adding);
/* Set visibility handler object */
dest->Visibility = adding ? qo : NULL;
/* User appears to vanish or appear from nowhere */
for (UCListIter f = dest->chans.begin(); f != dest->chans.end(); f++)
{
CUList *ulist = f->first->GetUsers();
char tb[MAXBUF];
snprintf(tb,MAXBUF,":%s %s %s", dest->GetFullHost(), adding ? "PART" : "JOIN", f->first->name);
std::string out = tb;
std::string n = this->ServerInstance->Modes->ModeString(dest, f->first);
for (CUList::iterator i = ulist->begin(); i != ulist->end(); i++)
{
/* User only appears to vanish for non-opers */
if (IS_LOCAL(i->first) && !IS_OPER(i->first))
{
i->first->Write(out);
if (!n.empty() && !adding)
i->first->WriteServ("MODE %s +%s", f->first->name, n.c_str());
}
}
ServerInstance->WriteOpers("*** \2NOTICE\2: Oper %s has become %svisible (%sQ)", dest->GetFullHost(), adding ? "in" : "", adding ? "+" : "-");
}
return MODEACTION_ALLOW;
}
else
{
return MODEACTION_DENY;
}
}
};
class InvisibleDeOper : public ModeWatcher
{
private:
InspIRCd* Srv;
public:
InvisibleDeOper(InspIRCd* Instance) : ModeWatcher(Instance, 'o', MODETYPE_USER), Srv(Instance)
{
}
bool BeforeMode(userrec* source, userrec* dest, chanrec* channel, std::string ¶m, bool adding, ModeType type)
{
/* Users who are opers and have +Q get their +Q removed when they deoper */
if ((!adding) && (dest->IsModeSet('Q')))
{
const char* newmodes[] = { dest->nick, "-Q" };
ServerInstance->Modes->Process(newmodes, 2, source, true);
}
return true;
}
};
class ModuleInvisible : public Module
{
private:
InvisibleMode* qm;
InvisibleDeOper* ido;
public:
ModuleInvisible(InspIRCd* Me)
: Module(Me)
{
conf = new ConfigReader(ServerInstance);
qm = new InvisibleMode(ServerInstance);
if (!ServerInstance->AddMode(qm, 'Q'))
throw ModuleException("Could not add new modes!");
ido = new InvisibleDeOper(ServerInstance);
if (!ServerInstance->AddModeWatcher(ido))
throw ModuleException("Could not add new mode watcher on usermode +o!");
}
virtual ~ModuleInvisible()
{
ServerInstance->Modes->DelMode(qm);
ServerInstance->Modes->DelModeWatcher(ido);
DELETE(qm);
DELETE(ido);
DELETE(conf);
}
virtual Version GetVersion()
{
return Version(1, 1, 0, 0, VF_COMMON | VF_VENDOR, API_VERSION);
}
void Implements(char* List)
{
List[I_OnUserPreMessage] = List[I_OnUserPreNotice] = List[I_OnUserJoin] = List[I_OnUserPart] = List[I_OnUserQuit] = List[I_OnRehash] = 1;
}
virtual void OnUserJoin(userrec* user, chanrec* channel, bool &silent)
{
if (user->IsModeSet('Q'))
{
silent = true;
/* Because we silenced the event, make sure it reaches the user whos joining (but only them of course) */
this->WriteCommonFrom(user, channel, "JOIN %s", channel->name);
ServerInstance->WriteOpers("*** \2NOTICE\2: Oper %s has joined %s invisibly (+Q)", user->GetFullHost(), channel->name);
}
}
virtual void OnRehash(userrec* user, const std::string ¶meter)
{
DELETE(conf);
conf = new ConfigReader(ServerInstance);
}
void OnUserPart(userrec* user, chanrec* channel, const std::string &partmessage, bool &silent)
{
if (user->IsModeSet('Q'))
{
silent = true;
/* Because we silenced the event, make sure it reaches the user whos leaving (but only them of course) */
this->WriteCommonFrom(user, channel, "PART %s%s%s", channel->name,
partmessage.empty() ? "" : " :",
partmessage.empty() ? "" : partmessage.c_str());
}
}
void OnUserQuit(userrec* user, const std::string &reason, const std::string &oper_message)
{
if (user->IsModeSet('Q'))
{
command_t* parthandler = ServerInstance->Parser->GetHandler("PART");
std::vector<std::string> to_leave;
const char* parameters[2];
if (parthandler)
{
for (UCListIter f = user->chans.begin(); f != user->chans.end(); f++)
to_leave.push_back(f->first->name);
/* We cant do this neatly in one loop, as we are modifying the map we are iterating */
for (std::vector<std::string>::iterator n = to_leave.begin(); n != to_leave.end(); n++)
{
parameters[0] = n->c_str();
/* This triggers our OnUserPart, above, making the PART silent */
parthandler->Handle(parameters, 1, user);
}
}
}
}
/* No privmsg response when hiding - submitted by Eric at neowin */
virtual int OnUserPreNotice(userrec* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list)
{
if ((target_type == TYPE_USER) && (IS_LOCAL(user)))
{
userrec* target = (userrec*)dest;
if(target->IsModeSet('Q') && !*user->oper)
{
user->WriteServ("401 %s %s :No such nick/channel",user->nick, target->nick);
return 1;
}
}
return 0;
}
virtual int OnUserPreMessage(userrec* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list)
{
return OnUserPreNotice(user, dest, target_type, text, status, exempt_list);
}
/* Fix by Eric @ neowin.net, thanks :) -- Brain */
void WriteCommonFrom(userrec *user, chanrec* channel, const char* text, ...)
{
va_list argsPtr;
char textbuffer[MAXBUF];
char tb[MAXBUF];
va_start(argsPtr, text);
vsnprintf(textbuffer, MAXBUF, text, argsPtr);
va_end(argsPtr);
snprintf(tb,MAXBUF,":%s %s",user->GetFullHost(),textbuffer);
CUList *ulist = channel->GetUsers();
for (CUList::iterator i = ulist->begin(); i != ulist->end(); i++)
{
/* User only appears to vanish for non-opers */
if (IS_LOCAL(i->first) && IS_OPER(i->first))
{
i->first->Write(std::string(tb));
}
}
}
};
MODULE_INIT(ModuleInvisible)
\ No newline at end of file +/* +------------------------------------+ + * | 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 "users.h" +#include "channels.h" +#include "modules.h" +#include <stdarg.h> + +/* $ModDesc: Allows for opered clients to join channels without being seen, similar to unreal 3.1 +I mode */ + +static ConfigReader* conf; + +class QuietOper : public VisData +{ + public: + QuietOper() + { + } + + virtual ~QuietOper() + { + } + + virtual bool VisibleTo(userrec* user) + { + return IS_OPER(user); + } +}; + + +class InvisibleMode : public ModeHandler +{ + QuietOper* qo; + public: + InvisibleMode(InspIRCd* Instance) : ModeHandler(Instance, 'Q', 0, 0, false, MODETYPE_USER, true) + { + qo = new QuietOper(); + } + + ~InvisibleMode() + { + for (user_hash::iterator i = ServerInstance->clientlist->begin(); i != ServerInstance->clientlist->end(); i++) + if (i->second->Visibility == qo) + i->second->Visibility = NULL; + delete qo; + } + + ModeAction OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string ¶meter, bool adding) + { + if (source != dest) + return MODEACTION_DENY; + + if (dest->IsModeSet('Q') != adding) + { + bool ok = false; + + for (int j = 0; j < conf->Enumerate("type"); j++) + { + std::string opertype = conf->ReadValue("type","name",j); + if (opertype == source->oper) + { + ok = conf->ReadFlag("type", "canquiet", j); + break; + } + } + + if (!ok) + { + source->WriteServ("481 %s :Permission Denied - You do not have access to become invisible via user mode +Q", source->nick); + return MODEACTION_DENY; + } + + dest->SetMode('Q', adding); + + /* Set visibility handler object */ + dest->Visibility = adding ? qo : NULL; + + /* User appears to vanish or appear from nowhere */ + for (UCListIter f = dest->chans.begin(); f != dest->chans.end(); f++) + { + CUList *ulist = f->first->GetUsers(); + char tb[MAXBUF]; + + snprintf(tb,MAXBUF,":%s %s %s", dest->GetFullHost(), adding ? "PART" : "JOIN", f->first->name); + std::string out = tb; + std::string n = this->ServerInstance->Modes->ModeString(dest, f->first); + + for (CUList::iterator i = ulist->begin(); i != ulist->end(); i++) + { + /* User only appears to vanish for non-opers */ + if (IS_LOCAL(i->first) && !IS_OPER(i->first)) + { + i->first->Write(out); + if (!n.empty() && !adding) + i->first->WriteServ("MODE %s +%s", f->first->name, n.c_str()); + } + } + + ServerInstance->WriteOpers("*** \2NOTICE\2: Oper %s has become %svisible (%sQ)", dest->GetFullHost(), adding ? "in" : "", adding ? "+" : "-"); + } + return MODEACTION_ALLOW; + } + else + { + return MODEACTION_DENY; + } + } +}; + +class InvisibleDeOper : public ModeWatcher +{ + private: + InspIRCd* Srv; + public: + InvisibleDeOper(InspIRCd* Instance) : ModeWatcher(Instance, 'o', MODETYPE_USER), Srv(Instance) + { + } + + bool BeforeMode(userrec* source, userrec* dest, chanrec* channel, std::string ¶m, bool adding, ModeType type) + { + /* Users who are opers and have +Q get their +Q removed when they deoper */ + if ((!adding) && (dest->IsModeSet('Q'))) + { + const char* newmodes[] = { dest->nick, "-Q" }; + ServerInstance->Modes->Process(newmodes, 2, source, true); + } + return true; + } +}; + + +class ModuleInvisible : public Module +{ + private: + InvisibleMode* qm; + InvisibleDeOper* ido; + public: + ModuleInvisible(InspIRCd* Me) + : Module(Me) + { + conf = new ConfigReader(ServerInstance); + qm = new InvisibleMode(ServerInstance); + if (!ServerInstance->AddMode(qm, 'Q')) + throw ModuleException("Could not add new modes!"); + ido = new InvisibleDeOper(ServerInstance); + if (!ServerInstance->AddModeWatcher(ido)) + throw ModuleException("Could not add new mode watcher on usermode +o!"); + } + + virtual ~ModuleInvisible() + { + ServerInstance->Modes->DelMode(qm); + ServerInstance->Modes->DelModeWatcher(ido); + DELETE(qm); + DELETE(ido); + DELETE(conf); + } + + virtual Version GetVersion() + { + return Version(1, 1, 0, 0, VF_COMMON | VF_VENDOR, API_VERSION); + } + + void Implements(char* List) + { + List[I_OnUserPreMessage] = List[I_OnUserPreNotice] = List[I_OnUserJoin] = List[I_OnUserPart] = List[I_OnUserQuit] = List[I_OnRehash] = 1; + } + + virtual void OnUserJoin(userrec* user, chanrec* channel, bool &silent) + { + if (user->IsModeSet('Q')) + { + silent = true; + /* Because we silenced the event, make sure it reaches the user whos joining (but only them of course) */ + this->WriteCommonFrom(user, channel, "JOIN %s", channel->name); + ServerInstance->WriteOpers("*** \2NOTICE\2: Oper %s has joined %s invisibly (+Q)", user->GetFullHost(), channel->name); + } + } + + virtual void OnRehash(userrec* user, const std::string ¶meter) + { + DELETE(conf); + conf = new ConfigReader(ServerInstance); + } + + void OnUserPart(userrec* user, chanrec* channel, const std::string &partmessage, bool &silent) + { + if (user->IsModeSet('Q')) + { + silent = true; + /* Because we silenced the event, make sure it reaches the user whos leaving (but only them of course) */ + this->WriteCommonFrom(user, channel, "PART %s%s%s", channel->name, + partmessage.empty() ? "" : " :", + partmessage.empty() ? "" : partmessage.c_str()); + } + } + + void OnUserQuit(userrec* user, const std::string &reason, const std::string &oper_message) + { + if (user->IsModeSet('Q')) + { + command_t* parthandler = ServerInstance->Parser->GetHandler("PART"); + std::vector<std::string> to_leave; + const char* parameters[2]; + if (parthandler) + { + for (UCListIter f = user->chans.begin(); f != user->chans.end(); f++) + to_leave.push_back(f->first->name); + /* We cant do this neatly in one loop, as we are modifying the map we are iterating */ + for (std::vector<std::string>::iterator n = to_leave.begin(); n != to_leave.end(); n++) + { + parameters[0] = n->c_str(); + /* This triggers our OnUserPart, above, making the PART silent */ + parthandler->Handle(parameters, 1, user); + } + } + } + } + + /* No privmsg response when hiding - submitted by Eric at neowin */ + virtual int OnUserPreNotice(userrec* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list) + { + if ((target_type == TYPE_USER) && (IS_LOCAL(user))) + { + userrec* target = (userrec*)dest; + if(target->IsModeSet('Q') && !*user->oper) + { + user->WriteServ("401 %s %s :No such nick/channel",user->nick, target->nick); + return 1; + } + } + return 0; + } + + virtual int OnUserPreMessage(userrec* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list) + { + return OnUserPreNotice(user, dest, target_type, text, status, exempt_list); + } + + /* Fix by Eric @ neowin.net, thanks :) -- Brain */ + void WriteCommonFrom(userrec *user, chanrec* channel, const char* text, ...) + { + va_list argsPtr; + char textbuffer[MAXBUF]; + char tb[MAXBUF]; + + va_start(argsPtr, text); + vsnprintf(textbuffer, MAXBUF, text, argsPtr); + va_end(argsPtr); + snprintf(tb,MAXBUF,":%s %s",user->GetFullHost(),textbuffer); + + CUList *ulist = channel->GetUsers(); + + for (CUList::iterator i = ulist->begin(); i != ulist->end(); i++) + { + /* User only appears to vanish for non-opers */ + if (IS_LOCAL(i->first) && IS_OPER(i->first)) + { + i->first->Write(std::string(tb)); + } + } + } + +}; + +MODULE_INIT(ModuleInvisible) diff --git a/src/modules/m_inviteexception.cpp b/src/modules/m_inviteexception.cpp index e51503b26..b7b9920c5 100644 --- a/src/modules/m_inviteexception.cpp +++ b/src/modules/m_inviteexception.cpp @@ -1 +1,150 @@ -/* +------------------------------------+
* | 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 "users.h"
#include "channels.h"
#include "modules.h"
#include "mode.h"
#include "u_listmode.h"
/* $ModDesc: Provides support for the +I channel mode */
/* $ModDep: ../../include/u_listmode.h */
/*
* Written by Om <om@inspircd.org>, April 2005.
* Based on m_exception, which was originally based on m_chanprotect and m_silence
*
* The +I channel mode takes a nick!ident@host, glob patterns allowed,
* and if a user matches an entry on the +I list then they can join the channel,
* ignoring if +i is set on the channel
* Now supports CIDR and IP addresses -- Brain
*/
class InspIRCd* ServerInstance;
/** Handles channel mode +I
*/
class InviteException : public ListModeBase
{
public:
InviteException(InspIRCd* Instance) : ListModeBase(Instance, 'I', "End of Channel Invite Exception List", "346", "347", true) { }
};
class ModuleInviteException : public Module
{
InviteException* ie;
public:
ModuleInviteException(InspIRCd* Me) : Module(Me)
{
ie = new InviteException(ServerInstance);
if (!ServerInstance->AddMode(ie, 'I'))
throw ModuleException("Could not add new modes!");
ServerInstance->PublishInterface("ChannelBanList", this);
}
virtual void Implements(char* List)
{
ie->DoImplements(List);
List[I_OnRequest] = List[I_On005Numeric] = List[I_OnCheckInvite] = 1;
}
virtual void On005Numeric(std::string &output)
{
output.append(" INVEX=I");
}
virtual int OnCheckInvite(userrec* user, chanrec* chan)
{
if(chan != NULL)
{
modelist* list;
chan->GetExt(ie->GetInfoKey(), list);
if (list)
{
char mask[MAXBUF];
snprintf(mask, MAXBUF, "%s!%s@%s", user->nick, user->ident, user->GetIPString());
for (modelist::iterator it = list->begin(); it != list->end(); it++)
{
if(match(user->GetFullRealHost(), it->mask.c_str()) || match(user->GetFullHost(), it->mask.c_str()) || (match(mask, it->mask.c_str(), true)))
{
// They match an entry on the list, so let them in.
return 1;
}
}
}
// or if there wasn't a list, there can't be anyone on it, so we don't need to do anything.
}
return 0;
}
virtual char* OnRequest(Request* request)
{
ListModeRequest* LM = (ListModeRequest*)request;
if (strcmp("LM_CHECKLIST", request->GetId()) == 0)
{
modelist* list;
LM->chan->GetExt(ie->GetInfoKey(), list);
if (list)
{
char mask[MAXBUF];
snprintf(mask, MAXBUF, "%s!%s@%s", LM->user->nick, LM->user->ident, LM->user->GetIPString());
for (modelist::iterator it = list->begin(); it != list->end(); it++)
{
if (match(LM->user->GetFullRealHost(), it->mask.c_str()) || match(LM->user->GetFullHost(), it->mask.c_str()) || (match(mask, it->mask.c_str(), true)))
{
// They match an entry
return (char*)it->mask.c_str();
}
}
return NULL;
}
}
return NULL;
}
virtual void OnCleanup(int target_type, void* item)
{
ie->DoCleanup(target_type, item);
}
virtual void OnSyncChannel(chanrec* chan, Module* proto, void* opaque)
{
ie->DoSyncChannel(chan, proto, opaque);
}
virtual void OnChannelDelete(chanrec* chan)
{
ie->DoChannelDelete(chan);
}
virtual void OnRehash(userrec* user, const std::string ¶m)
{
ie->DoRehash();
}
virtual Version GetVersion()
{
return Version(1, 1, 0, 3, VF_VENDOR | VF_COMMON, API_VERSION);
}
~ModuleInviteException()
{
ServerInstance->Modes->DelMode(ie);
DELETE(ie);
ServerInstance->UnpublishInterface("ChannelBanList", this);
}
};
MODULE_INIT(ModuleInviteException)
\ No newline at end of file +/* +------------------------------------+ + * | 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 "users.h" +#include "channels.h" +#include "modules.h" +#include "mode.h" +#include "u_listmode.h" + +/* $ModDesc: Provides support for the +I channel mode */ +/* $ModDep: ../../include/u_listmode.h */ + +/* + * Written by Om <om@inspircd.org>, April 2005. + * Based on m_exception, which was originally based on m_chanprotect and m_silence + * + * The +I channel mode takes a nick!ident@host, glob patterns allowed, + * and if a user matches an entry on the +I list then they can join the channel, + * ignoring if +i is set on the channel + * Now supports CIDR and IP addresses -- Brain + */ + +class InspIRCd* ServerInstance; + +/** Handles channel mode +I + */ +class InviteException : public ListModeBase +{ + public: + InviteException(InspIRCd* Instance) : ListModeBase(Instance, 'I', "End of Channel Invite Exception List", "346", "347", true) { } +}; + +class ModuleInviteException : public Module +{ + InviteException* ie; +public: + ModuleInviteException(InspIRCd* Me) : Module(Me) + { + ie = new InviteException(ServerInstance); + if (!ServerInstance->AddMode(ie, 'I')) + throw ModuleException("Could not add new modes!"); + ServerInstance->PublishInterface("ChannelBanList", this); + } + + virtual void Implements(char* List) + { + ie->DoImplements(List); + List[I_OnRequest] = List[I_On005Numeric] = List[I_OnCheckInvite] = 1; + } + + virtual void On005Numeric(std::string &output) + { + output.append(" INVEX=I"); + } + + virtual int OnCheckInvite(userrec* user, chanrec* chan) + { + if(chan != NULL) + { + modelist* list; + chan->GetExt(ie->GetInfoKey(), list); + if (list) + { + char mask[MAXBUF]; + snprintf(mask, MAXBUF, "%s!%s@%s", user->nick, user->ident, user->GetIPString()); + for (modelist::iterator it = list->begin(); it != list->end(); it++) + { + if(match(user->GetFullRealHost(), it->mask.c_str()) || match(user->GetFullHost(), it->mask.c_str()) || (match(mask, it->mask.c_str(), true))) + { + // They match an entry on the list, so let them in. + return 1; + } + } + } + // or if there wasn't a list, there can't be anyone on it, so we don't need to do anything. + } + + return 0; + } + + virtual char* OnRequest(Request* request) + { + ListModeRequest* LM = (ListModeRequest*)request; + if (strcmp("LM_CHECKLIST", request->GetId()) == 0) + { + modelist* list; + LM->chan->GetExt(ie->GetInfoKey(), list); + if (list) + { + char mask[MAXBUF]; + snprintf(mask, MAXBUF, "%s!%s@%s", LM->user->nick, LM->user->ident, LM->user->GetIPString()); + for (modelist::iterator it = list->begin(); it != list->end(); it++) + { + if (match(LM->user->GetFullRealHost(), it->mask.c_str()) || match(LM->user->GetFullHost(), it->mask.c_str()) || (match(mask, it->mask.c_str(), true))) + { + // They match an entry + return (char*)it->mask.c_str(); + } + } + return NULL; + } + } + return NULL; + } + + virtual void OnCleanup(int target_type, void* item) + { + ie->DoCleanup(target_type, item); + } + + virtual void OnSyncChannel(chanrec* chan, Module* proto, void* opaque) + { + ie->DoSyncChannel(chan, proto, opaque); + } + + virtual void OnChannelDelete(chanrec* chan) + { + ie->DoChannelDelete(chan); + } + + virtual void OnRehash(userrec* user, const std::string ¶m) + { + ie->DoRehash(); + } + + virtual Version GetVersion() + { + return Version(1, 1, 0, 3, VF_VENDOR | VF_COMMON, API_VERSION); + } + + ~ModuleInviteException() + { + ServerInstance->Modes->DelMode(ie); + DELETE(ie); + ServerInstance->UnpublishInterface("ChannelBanList", this); + } +}; + +MODULE_INIT(ModuleInviteException) diff --git a/src/modules/m_joinflood.cpp b/src/modules/m_joinflood.cpp index 26339e207..3d342b636 100644 --- a/src/modules/m_joinflood.cpp +++ b/src/modules/m_joinflood.cpp @@ -1 +1,285 @@ -/* +------------------------------------+
* | 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 "users.h"
#include "channels.h"
#include "modules.h"
/* $ModDesc: Provides channel mode +j (join flood protection) */
/** Holds settings and state associated with channel mode +j
*/
class joinfloodsettings : public classbase
{
public:
int secs;
int joins;
time_t reset;
time_t unlocktime;
int counter;
bool locked;
InspIRCd* ServerInstance;
joinfloodsettings() : secs(0), joins(0) {};
joinfloodsettings(int b, int c) : secs(b), joins(c)
{
reset = time(NULL) + secs;
counter = 0;
locked = false;
};
void addjoin()
{
counter++;
if (time(NULL) > reset)
{
counter = 0;
reset = time(NULL) + secs;
}
}
bool shouldlock()
{
return (counter >= this->joins);
}
void clear()
{
counter = 0;
}
bool islocked()
{
if (locked)
{
if (time(NULL) > unlocktime)
{
locked = false;
return false;
}
else
{
return true;
}
}
return false;
}
void lock()
{
locked = true;
unlocktime = time(NULL) + 60;
}
};
/** Handles channel mode +j
*/
class JoinFlood : public ModeHandler
{
public:
JoinFlood(InspIRCd* Instance) : ModeHandler(Instance, 'j', 1, 0, false, MODETYPE_CHANNEL, false) { }
ModePair ModeSet(userrec* source, userrec* dest, chanrec* channel, const std::string ¶meter)
{
joinfloodsettings* x;
if (channel->GetExt("joinflood",x))
return std::make_pair(true, ConvToStr(x->joins)+":"+ConvToStr(x->secs));
else
return std::make_pair(false, parameter);
}
bool CheckTimeStamp(time_t theirs, time_t ours, const std::string &their_param, const std::string &our_param, chanrec* channel)
{
/* When TS is equal, the alphabetically later one wins */
return (their_param < our_param);
}
ModeAction OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string ¶meter, bool adding)
{
joinfloodsettings* dummy;
if (adding)
{
char ndata[MAXBUF];
char* data = ndata;
strlcpy(ndata,parameter.c_str(),MAXBUF);
char* joins = data;
char* secs = NULL;
while (*data)
{
if (*data == ':')
{
*data = 0;
data++;
secs = data;
break;
}
else data++;
}
if (secs)
{
/* Set up the flood parameters for this channel */
int njoins = atoi(joins);
int nsecs = atoi(secs);
if ((njoins<1) || (nsecs<1))
{
source->WriteServ("608 %s %s :Invalid flood parameter",source->nick,channel->name);
parameter.clear();
return MODEACTION_DENY;
}
else
{
if (!channel->GetExt("joinflood", dummy))
{
parameter = ConvToStr(njoins) + ":" +ConvToStr(nsecs);
joinfloodsettings *f = new joinfloodsettings(nsecs,njoins);
channel->Extend("joinflood", f);
channel->SetMode('j', true);
channel->SetModeParam('j', parameter.c_str(), true);
return MODEACTION_ALLOW;
}
else
{
std::string cur_param = channel->GetModeParameter('j');
parameter = ConvToStr(njoins) + ":" +ConvToStr(nsecs);
if (cur_param == parameter)
{
// mode params match
return MODEACTION_DENY;
}
else
{
// new mode param, replace old with new
if ((nsecs > 0) && (njoins > 0))
{
joinfloodsettings* f;
channel->GetExt("joinflood", f);
delete f;
f = new joinfloodsettings(nsecs,njoins);
channel->Shrink("joinflood");
channel->Extend("joinflood", f);
channel->SetModeParam('j', cur_param.c_str(), false);
channel->SetModeParam('j', parameter.c_str(), true);
return MODEACTION_ALLOW;
}
else
{
return MODEACTION_DENY;
}
}
}
}
}
else
{
source->WriteServ("608 %s %s :Invalid flood parameter",source->nick,channel->name);
return MODEACTION_DENY;
}
}
else
{
if (channel->GetExt("joinflood", dummy))
{
joinfloodsettings *f;
channel->GetExt("joinflood", f);
DELETE(f);
channel->Shrink("joinflood");
channel->SetMode('j', false);
return MODEACTION_ALLOW;
}
}
return MODEACTION_DENY;
}
};
class ModuleJoinFlood : public Module
{
JoinFlood* jf;
public:
ModuleJoinFlood(InspIRCd* Me)
: Module(Me)
{
jf = new JoinFlood(ServerInstance);
if (!ServerInstance->AddMode(jf, 'j'))
throw ModuleException("Could not add new modes!");
}
virtual int OnUserPreJoin(userrec* user, chanrec* chan, const char* cname, std::string &privs)
{
if (chan)
{
joinfloodsettings *f;
if (chan->GetExt("joinflood", f))
{
if (f->islocked())
{
user->WriteServ("609 %s %s :This channel is temporarily unavailable (+j). Please try again later.",user->nick,chan->name);
return 1;
}
}
}
return 0;
}
virtual void OnUserJoin(userrec* user, chanrec* channel, bool &silent)
{
joinfloodsettings *f;
if (channel->GetExt("joinflood",f))
{
f->addjoin();
if (f->shouldlock())
{
f->clear();
f->lock();
channel->WriteChannelWithServ((char*)ServerInstance->Config->ServerName, "NOTICE %s :This channel has been closed to new users for 60 seconds because there have been more than %d joins in %d seconds.", channel->name, f->joins, f->secs);
}
}
}
void OnChannelDelete(chanrec* chan)
{
joinfloodsettings *f;
if (chan->GetExt("joinflood",f))
{
DELETE(f);
chan->Shrink("joinflood");
}
}
void Implements(char* List)
{
List[I_OnChannelDelete] = List[I_OnUserPreJoin] = List[I_OnUserJoin] = 1;
}
virtual ~ModuleJoinFlood()
{
ServerInstance->Modes->DelMode(jf);
DELETE(jf);
}
virtual Version GetVersion()
{
return Version(1, 1, 0, 0, VF_COMMON | VF_VENDOR, API_VERSION);
}
};
MODULE_INIT(ModuleJoinFlood)
\ No newline at end of file +/* +------------------------------------+ + * | 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 "users.h" +#include "channels.h" +#include "modules.h" + +/* $ModDesc: Provides channel mode +j (join flood protection) */ + +/** Holds settings and state associated with channel mode +j + */ +class joinfloodsettings : public classbase +{ + public: + + int secs; + int joins; + time_t reset; + time_t unlocktime; + int counter; + bool locked; + InspIRCd* ServerInstance; + + joinfloodsettings() : secs(0), joins(0) {}; + + joinfloodsettings(int b, int c) : secs(b), joins(c) + { + reset = time(NULL) + secs; + counter = 0; + locked = false; + }; + + void addjoin() + { + counter++; + if (time(NULL) > reset) + { + counter = 0; + reset = time(NULL) + secs; + } + } + + bool shouldlock() + { + return (counter >= this->joins); + } + + void clear() + { + counter = 0; + } + + bool islocked() + { + if (locked) + { + if (time(NULL) > unlocktime) + { + locked = false; + return false; + } + else + { + return true; + } + } + return false; + } + + void lock() + { + locked = true; + unlocktime = time(NULL) + 60; + } + +}; + +/** Handles channel mode +j + */ +class JoinFlood : public ModeHandler +{ + public: + JoinFlood(InspIRCd* Instance) : ModeHandler(Instance, 'j', 1, 0, false, MODETYPE_CHANNEL, false) { } + + ModePair ModeSet(userrec* source, userrec* dest, chanrec* channel, const std::string ¶meter) + { + joinfloodsettings* x; + if (channel->GetExt("joinflood",x)) + return std::make_pair(true, ConvToStr(x->joins)+":"+ConvToStr(x->secs)); + else + return std::make_pair(false, parameter); + } + + bool CheckTimeStamp(time_t theirs, time_t ours, const std::string &their_param, const std::string &our_param, chanrec* channel) + { + /* When TS is equal, the alphabetically later one wins */ + return (their_param < our_param); + } + + ModeAction OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string ¶meter, bool adding) + { + joinfloodsettings* dummy; + + if (adding) + { + char ndata[MAXBUF]; + char* data = ndata; + strlcpy(ndata,parameter.c_str(),MAXBUF); + char* joins = data; + char* secs = NULL; + while (*data) + { + if (*data == ':') + { + *data = 0; + data++; + secs = data; + break; + } + else data++; + } + if (secs) + + { + /* Set up the flood parameters for this channel */ + int njoins = atoi(joins); + int nsecs = atoi(secs); + if ((njoins<1) || (nsecs<1)) + { + source->WriteServ("608 %s %s :Invalid flood parameter",source->nick,channel->name); + parameter.clear(); + return MODEACTION_DENY; + } + else + { + if (!channel->GetExt("joinflood", dummy)) + { + parameter = ConvToStr(njoins) + ":" +ConvToStr(nsecs); + joinfloodsettings *f = new joinfloodsettings(nsecs,njoins); + channel->Extend("joinflood", f); + channel->SetMode('j', true); + channel->SetModeParam('j', parameter.c_str(), true); + return MODEACTION_ALLOW; + } + else + { + std::string cur_param = channel->GetModeParameter('j'); + parameter = ConvToStr(njoins) + ":" +ConvToStr(nsecs); + if (cur_param == parameter) + { + // mode params match + return MODEACTION_DENY; + } + else + { + // new mode param, replace old with new + if ((nsecs > 0) && (njoins > 0)) + { + joinfloodsettings* f; + channel->GetExt("joinflood", f); + delete f; + f = new joinfloodsettings(nsecs,njoins); + channel->Shrink("joinflood"); + channel->Extend("joinflood", f); + channel->SetModeParam('j', cur_param.c_str(), false); + channel->SetModeParam('j', parameter.c_str(), true); + return MODEACTION_ALLOW; + } + else + { + return MODEACTION_DENY; + } + } + } + } + } + else + { + source->WriteServ("608 %s %s :Invalid flood parameter",source->nick,channel->name); + return MODEACTION_DENY; + } + } + else + { + if (channel->GetExt("joinflood", dummy)) + { + joinfloodsettings *f; + channel->GetExt("joinflood", f); + DELETE(f); + channel->Shrink("joinflood"); + channel->SetMode('j', false); + return MODEACTION_ALLOW; + } + } + return MODEACTION_DENY; + } +}; + +class ModuleJoinFlood : public Module +{ + + JoinFlood* jf; + + public: + + ModuleJoinFlood(InspIRCd* Me) + : Module(Me) + { + + jf = new JoinFlood(ServerInstance); + if (!ServerInstance->AddMode(jf, 'j')) + throw ModuleException("Could not add new modes!"); + } + + virtual int OnUserPreJoin(userrec* user, chanrec* chan, const char* cname, std::string &privs) + { + if (chan) + { + joinfloodsettings *f; + if (chan->GetExt("joinflood", f)) + { + if (f->islocked()) + { + user->WriteServ("609 %s %s :This channel is temporarily unavailable (+j). Please try again later.",user->nick,chan->name); + return 1; + } + } + } + return 0; + } + + virtual void OnUserJoin(userrec* user, chanrec* channel, bool &silent) + { + joinfloodsettings *f; + if (channel->GetExt("joinflood",f)) + { + f->addjoin(); + if (f->shouldlock()) + { + f->clear(); + f->lock(); + channel->WriteChannelWithServ((char*)ServerInstance->Config->ServerName, "NOTICE %s :This channel has been closed to new users for 60 seconds because there have been more than %d joins in %d seconds.", channel->name, f->joins, f->secs); + } + } + } + + void OnChannelDelete(chanrec* chan) + { + joinfloodsettings *f; + if (chan->GetExt("joinflood",f)) + { + DELETE(f); + chan->Shrink("joinflood"); + } + } + + void Implements(char* List) + { + List[I_OnChannelDelete] = List[I_OnUserPreJoin] = List[I_OnUserJoin] = 1; + } + + virtual ~ModuleJoinFlood() + { + ServerInstance->Modes->DelMode(jf); + DELETE(jf); + } + + virtual Version GetVersion() + { + return Version(1, 1, 0, 0, VF_COMMON | VF_VENDOR, API_VERSION); + } +}; + +MODULE_INIT(ModuleJoinFlood) diff --git a/src/modules/m_jumpserver.cpp b/src/modules/m_jumpserver.cpp index 5a823b44c..28bbd056e 100644 --- a/src/modules/m_jumpserver.cpp +++ b/src/modules/m_jumpserver.cpp @@ -1 +1,164 @@ -/* +------------------------------------+
* | 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 "users.h"
#include "channels.h"
#include "modules.h"
/* $ModDesc: Provides support for unreal-style SAPART command */
/** Handle /SAPART
*/
class cmd_jumpserver : public command_t
{
public:
bool redirect_all_immediately;
bool redirect_new_users;
bool direction;
std::string redirect_to;
std::string reason;
int port;
cmd_jumpserver (InspIRCd* Instance) : command_t(Instance, "JUMPSERVER", 'o', 0)
{
this->source = "m_jumpserver.so";
syntax = "[<server> <port> <+/-a> :<reason>]";
redirect_to.clear();
reason.clear();
port = 0;
redirect_all_immediately = redirect_new_users = false;
}
CmdResult Handle (const char** parameters, int pcnt, userrec *user)
{
int n_done = 0;
reason = (pcnt < 4) ? "Please use this server/port instead" : parameters[3];
redirect_all_immediately = false;
redirect_new_users = true;
direction = true;
std::string n_done_s;
/* No parameters: jumpserver disabled */
if (!pcnt)
{
if (port)
user->WriteServ("NOTICE %s :*** Disabled jumpserver (previously set to '%s:%d')", user->nick, redirect_to.c_str(), port);
else
user->WriteServ("NOTICE %s :*** jumpserver was not enabled.", user->nick);
port = 0;
redirect_to.clear();
return CMD_LOCALONLY;
}
port = 0;
redirect_to.clear();
for (const char* n = parameters[2]; *n; n++)
{
switch (*n)
{
case '+':
direction = true;
break;
case '-':
direction = false;
break;
case 'a':
redirect_all_immediately = direction;
break;
case 'n':
redirect_new_users = direction;
break;
}
}
if (redirect_all_immediately)
{
/* Redirect everyone but the oper sending the command */
for (std::vector<userrec*>::const_iterator i = ServerInstance->local_users.begin(); i != ServerInstance->local_users.end(); i++)
{
userrec* t = *i;
if (!IS_OPER(t))
{
t->WriteServ("010 %s %s %s :Please use this Server/Port instead", user->nick, parameters[0], parameters[1]);
userrec::QuitUser(ServerInstance, t, reason);
n_done++;
}
}
if (n_done)
{
n_done_s = ConvToStr(n_done);
}
}
if (redirect_new_users)
{
redirect_to = parameters[0];
port = atoi(parameters[1]);
}
user->WriteServ("NOTICE %s :*** Set jumpserver to server '%s' port '%s', flags '+%s%s'%s%s%s: %s", user->nick, parameters[0], parameters[1],
redirect_all_immediately ? "a" : "",
redirect_new_users ? "n" : "",
n_done ? " (" : "",
n_done ? n_done_s.c_str() : "",
n_done ? " user(s) redirected)" : "",
reason.c_str());
return CMD_LOCALONLY;
}
};
class ModuleJumpServer : public Module
{
cmd_jumpserver* js;
public:
ModuleJumpServer(InspIRCd* Me)
: Module(Me)
{
js = new cmd_jumpserver(ServerInstance);
ServerInstance->AddCommand(js);
}
virtual ~ModuleJumpServer()
{
}
virtual int OnUserRegister(userrec* user)
{
if (js->port && js->redirect_new_users)
{
user->WriteServ("010 %s %s %d :Please use this Server/Port instead", user->nick, js->redirect_to.c_str(), js->port);
userrec::QuitUser(ServerInstance, user, js->reason);
return 0;
}
return 0;
}
virtual void Implements(char* List)
{
List[I_OnUserRegister] = 1;
}
virtual Version GetVersion()
{
return Version(1,1,0,1,VF_VENDOR,API_VERSION);
}
};
MODULE_INIT(ModuleJumpServer)
\ No newline at end of file +/* +------------------------------------+ + * | 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 "users.h" +#include "channels.h" +#include "modules.h" + +/* $ModDesc: Provides support for unreal-style SAPART command */ + +/** Handle /SAPART + */ +class cmd_jumpserver : public command_t +{ + public: + bool redirect_all_immediately; + bool redirect_new_users; + bool direction; + std::string redirect_to; + std::string reason; + int port; + + cmd_jumpserver (InspIRCd* Instance) : command_t(Instance, "JUMPSERVER", 'o', 0) + { + this->source = "m_jumpserver.so"; + syntax = "[<server> <port> <+/-a> :<reason>]"; + redirect_to.clear(); + reason.clear(); + port = 0; + redirect_all_immediately = redirect_new_users = false; + } + + CmdResult Handle (const char** parameters, int pcnt, userrec *user) + { + int n_done = 0; + reason = (pcnt < 4) ? "Please use this server/port instead" : parameters[3]; + redirect_all_immediately = false; + redirect_new_users = true; + direction = true; + std::string n_done_s; + + /* No parameters: jumpserver disabled */ + if (!pcnt) + { + if (port) + user->WriteServ("NOTICE %s :*** Disabled jumpserver (previously set to '%s:%d')", user->nick, redirect_to.c_str(), port); + else + user->WriteServ("NOTICE %s :*** jumpserver was not enabled.", user->nick); + + port = 0; + redirect_to.clear(); + return CMD_LOCALONLY; + } + + port = 0; + redirect_to.clear(); + + for (const char* n = parameters[2]; *n; n++) + { + switch (*n) + { + case '+': + direction = true; + break; + case '-': + direction = false; + break; + case 'a': + redirect_all_immediately = direction; + break; + case 'n': + redirect_new_users = direction; + break; + } + } + + if (redirect_all_immediately) + { + /* Redirect everyone but the oper sending the command */ + for (std::vector<userrec*>::const_iterator i = ServerInstance->local_users.begin(); i != ServerInstance->local_users.end(); i++) + { + userrec* t = *i; + if (!IS_OPER(t)) + { + t->WriteServ("010 %s %s %s :Please use this Server/Port instead", user->nick, parameters[0], parameters[1]); + userrec::QuitUser(ServerInstance, t, reason); + n_done++; + } + } + if (n_done) + { + n_done_s = ConvToStr(n_done); + } + } + + if (redirect_new_users) + { + redirect_to = parameters[0]; + port = atoi(parameters[1]); + } + + user->WriteServ("NOTICE %s :*** Set jumpserver to server '%s' port '%s', flags '+%s%s'%s%s%s: %s", user->nick, parameters[0], parameters[1], + redirect_all_immediately ? "a" : "", + redirect_new_users ? "n" : "", + n_done ? " (" : "", + n_done ? n_done_s.c_str() : "", + n_done ? " user(s) redirected)" : "", + reason.c_str()); + + return CMD_LOCALONLY; + } +}; + + +class ModuleJumpServer : public Module +{ + cmd_jumpserver* js; + public: + ModuleJumpServer(InspIRCd* Me) + : Module(Me) + { + + js = new cmd_jumpserver(ServerInstance); + ServerInstance->AddCommand(js); + } + + virtual ~ModuleJumpServer() + { + } + + virtual int OnUserRegister(userrec* user) + { + if (js->port && js->redirect_new_users) + { + user->WriteServ("010 %s %s %d :Please use this Server/Port instead", user->nick, js->redirect_to.c_str(), js->port); + userrec::QuitUser(ServerInstance, user, js->reason); + return 0; + } + return 0; + } + + virtual void Implements(char* List) + { + List[I_OnUserRegister] = 1; + } + + virtual Version GetVersion() + { + return Version(1,1,0,1,VF_VENDOR,API_VERSION); + } + +}; + +MODULE_INIT(ModuleJumpServer) diff --git a/src/modules/m_kicknorejoin.cpp b/src/modules/m_kicknorejoin.cpp index 97d88786f..bdb988ad2 100644 --- a/src/modules/m_kicknorejoin.cpp +++ b/src/modules/m_kicknorejoin.cpp @@ -1 +1,224 @@ -/* +------------------------------------+
* | 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 <sstream>
#include "users.h"
#include "channels.h"
#include "modules.h"
/* $ModDesc: Provides channel mode +J (delay rejoin after kick) */
inline int strtoint(const std::string &str)
{
std::istringstream ss(str);
int result;
ss >> result;
return result;
}
typedef std::map<userrec*, time_t> delaylist;
/** Handles channel mode +J
*/
class KickRejoin : public ModeHandler
{
public:
KickRejoin(InspIRCd* Instance) : ModeHandler(Instance, 'J', 1, 0, false, MODETYPE_CHANNEL, false) { }
ModePair ModeSet(userrec* source, userrec* dest, chanrec* channel, const std::string ¶meter)
{
if (channel->IsModeSet('J'))
return std::make_pair(true, channel->GetModeParameter('J'));
else
return std::make_pair(false, parameter);
}
bool CheckTimeStamp(time_t theirs, time_t ours, const std::string &their_param, const std::string &our_param, chanrec* channel)
{
/* When TS is equal, the alphabetically later one wins */
return (their_param < our_param);
}
ModeAction OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string ¶meter, bool adding)
{
if (!adding)
{
// Taking the mode off, we need to clean up.
delaylist* dl;
if (channel->GetExt("norejoinusers", dl))
{
DELETE(dl);
channel->Shrink("norejoinusers");
}
if (!channel->IsModeSet('J'))
{
return MODEACTION_DENY;
}
else
{
channel->SetMode('J', false);
return MODEACTION_ALLOW;
}
}
else if (atoi(parameter.c_str()) > 0)
{
if (!channel->IsModeSet('J'))
{
parameter = ConvToStr(atoi(parameter.c_str()));
channel->SetModeParam('J', parameter.c_str(), adding);
channel->SetMode('J', adding);
return MODEACTION_ALLOW;
}
else
{
std::string cur_param = channel->GetModeParameter('J');
if (cur_param == parameter)
{
// mode params match, don't change mode
return MODEACTION_DENY;
}
else
{
// new mode param, replace old with new
parameter = ConvToStr(atoi(parameter.c_str()));
cur_param = ConvToStr(atoi(cur_param.c_str()));
if (parameter != "0")
{
channel->SetModeParam('J', cur_param.c_str(), false);
channel->SetModeParam('J', parameter.c_str(), adding);
return MODEACTION_ALLOW;
}
else
{
/* Fix to jamie's fix, dont allow +J 0 on the new value! */
return MODEACTION_DENY;
}
}
}
}
else
{
return MODEACTION_DENY;
}
}
};
class ModuleKickNoRejoin : public Module
{
KickRejoin* kr;
public:
ModuleKickNoRejoin(InspIRCd* Me)
: Module(Me)
{
kr = new KickRejoin(ServerInstance);
if (!ServerInstance->AddMode(kr, 'J'))
throw ModuleException("Could not add new modes!");
}
virtual int OnUserPreJoin(userrec* user, chanrec* chan, const char* cname, std::string &privs)
{
if (chan)
{
delaylist* dl;
if (chan->GetExt("norejoinusers", dl))
{
std::vector<userrec*> itemstoremove;
for (delaylist::iterator iter = dl->begin(); iter != dl->end(); iter++)
{
if (iter->second > time(NULL))
{
if (iter->first == user)
{
user->WriteServ( "495 %s %s :You cannot rejoin this channel yet after being kicked (+J)", user->nick, chan->name);
return 1;
}
}
else
{
// Expired record, remove.
itemstoremove.push_back(iter->first);
}
}
for (unsigned int i = 0; i < itemstoremove.size(); i++)
dl->erase(itemstoremove[i]);
if (!dl->size())
{
// Now it's empty..
DELETE(dl);
chan->Shrink("norejoinusers");
}
}
}
return 0;
}
virtual void OnUserKick(userrec* source, userrec* user, chanrec* chan, const std::string &reason, bool &silent)
{
if (chan->IsModeSet('J') && (source != user))
{
delaylist* dl;
if (!chan->GetExt("norejoinusers", dl))
{
dl = new delaylist;
chan->Extend("norejoinusers", dl);
}
(*dl)[user] = time(NULL) + strtoint(chan->GetModeParameter('J'));
}
}
virtual void OnChannelDelete(chanrec* chan)
{
delaylist* dl;
if (chan->GetExt("norejoinusers", dl))
{
DELETE(dl);
chan->Shrink("norejoinusers");
}
}
virtual void OnCleanup(int target_type, void* item)
{
if(target_type == TYPE_CHANNEL)
OnChannelDelete((chanrec*)item);
}
virtual void Implements(char* List)
{
List[I_OnCleanup] = List[I_OnChannelDelete] = List[I_OnUserPreJoin] = List[I_OnUserKick] = 1;
}
virtual ~ModuleKickNoRejoin()
{
ServerInstance->Modes->DelMode(kr);
DELETE(kr);
}
virtual Version GetVersion()
{
return Version(1, 1, 0, 0, VF_COMMON | VF_VENDOR, API_VERSION);
}
};
MODULE_INIT(ModuleKickNoRejoin)
\ No newline at end of file +/* +------------------------------------+ + * | 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 <sstream> +#include "users.h" +#include "channels.h" +#include "modules.h" + +/* $ModDesc: Provides channel mode +J (delay rejoin after kick) */ + +inline int strtoint(const std::string &str) +{ + std::istringstream ss(str); + int result; + ss >> result; + return result; +} + +typedef std::map<userrec*, time_t> delaylist; + +/** Handles channel mode +J + */ +class KickRejoin : public ModeHandler +{ + public: + KickRejoin(InspIRCd* Instance) : ModeHandler(Instance, 'J', 1, 0, false, MODETYPE_CHANNEL, false) { } + + ModePair ModeSet(userrec* source, userrec* dest, chanrec* channel, const std::string ¶meter) + { + if (channel->IsModeSet('J')) + return std::make_pair(true, channel->GetModeParameter('J')); + else + return std::make_pair(false, parameter); + } + + bool CheckTimeStamp(time_t theirs, time_t ours, const std::string &their_param, const std::string &our_param, chanrec* channel) + { + /* When TS is equal, the alphabetically later one wins */ + return (their_param < our_param); + } + + ModeAction OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string ¶meter, bool adding) + { + if (!adding) + { + // Taking the mode off, we need to clean up. + delaylist* dl; + + if (channel->GetExt("norejoinusers", dl)) + { + DELETE(dl); + channel->Shrink("norejoinusers"); + } + + if (!channel->IsModeSet('J')) + { + return MODEACTION_DENY; + } + else + { + channel->SetMode('J', false); + return MODEACTION_ALLOW; + } + } + else if (atoi(parameter.c_str()) > 0) + { + if (!channel->IsModeSet('J')) + { + parameter = ConvToStr(atoi(parameter.c_str())); + channel->SetModeParam('J', parameter.c_str(), adding); + channel->SetMode('J', adding); + return MODEACTION_ALLOW; + } + else + { + std::string cur_param = channel->GetModeParameter('J'); + if (cur_param == parameter) + { + // mode params match, don't change mode + return MODEACTION_DENY; + } + else + { + // new mode param, replace old with new + parameter = ConvToStr(atoi(parameter.c_str())); + cur_param = ConvToStr(atoi(cur_param.c_str())); + if (parameter != "0") + { + channel->SetModeParam('J', cur_param.c_str(), false); + channel->SetModeParam('J', parameter.c_str(), adding); + return MODEACTION_ALLOW; + } + else + { + /* Fix to jamie's fix, dont allow +J 0 on the new value! */ + return MODEACTION_DENY; + } + } + } + } + else + { + return MODEACTION_DENY; + } + } +}; + +class ModuleKickNoRejoin : public Module +{ + + KickRejoin* kr; + +public: + + ModuleKickNoRejoin(InspIRCd* Me) + : Module(Me) + { + + kr = new KickRejoin(ServerInstance); + if (!ServerInstance->AddMode(kr, 'J')) + throw ModuleException("Could not add new modes!"); + } + + virtual int OnUserPreJoin(userrec* user, chanrec* chan, const char* cname, std::string &privs) + { + if (chan) + { + delaylist* dl; + if (chan->GetExt("norejoinusers", dl)) + { + std::vector<userrec*> itemstoremove; + + for (delaylist::iterator iter = dl->begin(); iter != dl->end(); iter++) + { + if (iter->second > time(NULL)) + { + if (iter->first == user) + { + user->WriteServ( "495 %s %s :You cannot rejoin this channel yet after being kicked (+J)", user->nick, chan->name); + return 1; + } + } + else + { + // Expired record, remove. + itemstoremove.push_back(iter->first); + } + } + + for (unsigned int i = 0; i < itemstoremove.size(); i++) + dl->erase(itemstoremove[i]); + + if (!dl->size()) + { + // Now it's empty.. + DELETE(dl); + chan->Shrink("norejoinusers"); + } + } + } + return 0; + } + + virtual void OnUserKick(userrec* source, userrec* user, chanrec* chan, const std::string &reason, bool &silent) + { + if (chan->IsModeSet('J') && (source != user)) + { + delaylist* dl; + if (!chan->GetExt("norejoinusers", dl)) + { + dl = new delaylist; + chan->Extend("norejoinusers", dl); + } + (*dl)[user] = time(NULL) + strtoint(chan->GetModeParameter('J')); + } + } + + virtual void OnChannelDelete(chanrec* chan) + { + delaylist* dl; + + if (chan->GetExt("norejoinusers", dl)) + { + DELETE(dl); + chan->Shrink("norejoinusers"); + } + } + + virtual void OnCleanup(int target_type, void* item) + { + if(target_type == TYPE_CHANNEL) + OnChannelDelete((chanrec*)item); + } + + virtual void Implements(char* List) + { + List[I_OnCleanup] = List[I_OnChannelDelete] = List[I_OnUserPreJoin] = List[I_OnUserKick] = 1; + } + + virtual ~ModuleKickNoRejoin() + { + ServerInstance->Modes->DelMode(kr); + DELETE(kr); + } + + virtual Version GetVersion() + { + return Version(1, 1, 0, 0, VF_COMMON | VF_VENDOR, API_VERSION); + } +}; + + +MODULE_INIT(ModuleKickNoRejoin) diff --git a/src/modules/m_knock.cpp b/src/modules/m_knock.cpp index 9beaa699e..3bc45cceb 100644 --- a/src/modules/m_knock.cpp +++ b/src/modules/m_knock.cpp @@ -1 +1,129 @@ -/* +------------------------------------+
* | 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 "users.h"
#include "channels.h"
#include "modules.h"
/* $ModDesc: Provides support for /KNOCK and mode +K */
/** Handles the /KNOCK command
*/
class cmd_knock : public command_t
{
public:
cmd_knock (InspIRCd* Instance) : command_t(Instance,"KNOCK", 0, 2)
{
this->source = "m_knock.so";
syntax = "<channel> <reason>";
}
CmdResult Handle (const char** parameters, int pcnt, userrec *user)
{
chanrec* c = ServerInstance->FindChan(parameters[0]);
if (!c)
{
user->WriteServ("401 %s %s :No such channel",user->nick, parameters[0]);
return CMD_FAILURE;
}
std::string line;
if (c->IsModeSet('K'))
{
user->WriteServ("480 %s :Can't KNOCK on %s, +K is set.",user->nick, c->name);
return CMD_FAILURE;
}
for (int i = 1; i < pcnt - 1; i++)
{
line = line + std::string(parameters[i]) + " ";
}
line = line + std::string(parameters[pcnt-1]);
if (!c->modes[CM_INVITEONLY])
{
user->WriteServ("480 %s :Can't KNOCK on %s, channel is not invite only so knocking is pointless!",user->nick, c->name);
return CMD_FAILURE;
}
c->WriteChannelWithServ((char*)ServerInstance->Config->ServerName, "NOTICE %s :User %s is KNOCKing on %s (%s)", c->name, user->nick, c->name, line.c_str());
user->WriteServ("NOTICE %s :KNOCKing on %s",user->nick,c->name);
return CMD_SUCCESS;
}
};
/** Handles channel mode +K
*/
class Knock : public ModeHandler
{
public:
Knock(InspIRCd* Instance) : ModeHandler(Instance, 'K', 0, 0, false, MODETYPE_CHANNEL, false) { }
ModeAction OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string ¶meter, bool adding)
{
if (adding)
{
if (!channel->IsModeSet('K'))
{
channel->SetMode('K',true);
return MODEACTION_ALLOW;
}
}
else
{
if (channel->IsModeSet('K'))
{
channel->SetMode('K',false);
return MODEACTION_ALLOW;
}
}
return MODEACTION_DENY;
}
};
class ModuleKnock : public Module
{
cmd_knock* mycommand;
Knock* kn;
public:
ModuleKnock(InspIRCd* Me) : Module(Me)
{
kn = new Knock(ServerInstance);
if (!ServerInstance->AddMode(kn, 'K'))
throw ModuleException("Could not add new modes!");
mycommand = new cmd_knock(ServerInstance);
ServerInstance->AddCommand(mycommand);
}
void Implements(char* List)
{
}
virtual ~ModuleKnock()
{
ServerInstance->Modes->DelMode(kn);
DELETE(kn);
}
virtual Version GetVersion()
{
return Version(1,1,0,1,VF_COMMON|VF_VENDOR,API_VERSION);
}
};
MODULE_INIT(ModuleKnock)
\ No newline at end of file +/* +------------------------------------+ + * | 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 "users.h" +#include "channels.h" +#include "modules.h" + +/* $ModDesc: Provides support for /KNOCK and mode +K */ + +/** Handles the /KNOCK command + */ +class cmd_knock : public command_t +{ + public: + cmd_knock (InspIRCd* Instance) : command_t(Instance,"KNOCK", 0, 2) + { + this->source = "m_knock.so"; + syntax = "<channel> <reason>"; + } + + CmdResult Handle (const char** parameters, int pcnt, userrec *user) + { + chanrec* c = ServerInstance->FindChan(parameters[0]); + + if (!c) + { + user->WriteServ("401 %s %s :No such channel",user->nick, parameters[0]); + return CMD_FAILURE; + } + + std::string line; + + if (c->IsModeSet('K')) + { + user->WriteServ("480 %s :Can't KNOCK on %s, +K is set.",user->nick, c->name); + return CMD_FAILURE; + } + + for (int i = 1; i < pcnt - 1; i++) + { + line = line + std::string(parameters[i]) + " "; + } + line = line + std::string(parameters[pcnt-1]); + + if (!c->modes[CM_INVITEONLY]) + { + user->WriteServ("480 %s :Can't KNOCK on %s, channel is not invite only so knocking is pointless!",user->nick, c->name); + return CMD_FAILURE; + } + + c->WriteChannelWithServ((char*)ServerInstance->Config->ServerName, "NOTICE %s :User %s is KNOCKing on %s (%s)", c->name, user->nick, c->name, line.c_str()); + user->WriteServ("NOTICE %s :KNOCKing on %s",user->nick,c->name); + return CMD_SUCCESS; + } +}; + +/** Handles channel mode +K + */ +class Knock : public ModeHandler +{ + public: + Knock(InspIRCd* Instance) : ModeHandler(Instance, 'K', 0, 0, false, MODETYPE_CHANNEL, false) { } + + ModeAction OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string ¶meter, bool adding) + { + if (adding) + { + if (!channel->IsModeSet('K')) + { + channel->SetMode('K',true); + return MODEACTION_ALLOW; + } + } + else + { + if (channel->IsModeSet('K')) + { + channel->SetMode('K',false); + return MODEACTION_ALLOW; + } + } + + return MODEACTION_DENY; + } +}; + +class ModuleKnock : public Module +{ + cmd_knock* mycommand; + Knock* kn; + public: + ModuleKnock(InspIRCd* Me) : Module(Me) + { + + kn = new Knock(ServerInstance); + if (!ServerInstance->AddMode(kn, 'K')) + throw ModuleException("Could not add new modes!"); + mycommand = new cmd_knock(ServerInstance); + ServerInstance->AddCommand(mycommand); + } + + void Implements(char* List) + { + } + + virtual ~ModuleKnock() + { + ServerInstance->Modes->DelMode(kn); + DELETE(kn); + } + + virtual Version GetVersion() + { + return Version(1,1,0,1,VF_COMMON|VF_VENDOR,API_VERSION); + } +}; + +MODULE_INIT(ModuleKnock) diff --git a/src/modules/m_lockserv.cpp b/src/modules/m_lockserv.cpp index d83961233..2ca2e3f44 100644 --- a/src/modules/m_lockserv.cpp +++ b/src/modules/m_lockserv.cpp @@ -1 +1,131 @@ -/* +------------------------------------+
* | 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 "users.h"
#include "channels.h"
#include "modules.h"
/* $ModDesc: Allows locking of the server to stop all incoming connections till unlocked again */
/** Adds numerics
* 988 <nick> <servername> :Closed for new connections
* 989 <nick> <servername> :Open for new connections
*/
class cmd_lockserv : public command_t
{
private:
bool& locked;
public:
cmd_lockserv (InspIRCd* Instance, bool &lock)
: command_t(Instance, "LOCKSERV", 'o', 0), locked(lock)
{
this->source = "m_lockserv.so";
syntax.clear();
}
CmdResult Handle (const char** parameters, int pcnt, userrec *user)
{
locked = true;
user->WriteServ("988 %s %s :Closed for new connections", user->nick, user->server);
ServerInstance->WriteOpers("*** Oper %s used LOCKSERV to temporarily close for new connections", user->nick);
/* Dont send to the network */
return CMD_LOCALONLY;
}
};
class cmd_unlockserv : public command_t
{
private:
bool& locked;
public:
cmd_unlockserv (InspIRCd* Instance, bool &lock)
: command_t(Instance, "UNLOCKSERV", 'o', 0), locked(lock)
{
this->source = "m_lockserv.so";
syntax.clear();
}
CmdResult Handle (const char** parameters, int pcnt, userrec *user)
{
locked = false;
user->WriteServ("989 %s %s :Open for new connections", user->nick, user->server);
ServerInstance->WriteOpers("*** Oper %s used UNLOCKSERV to allow for new connections", user->nick);
/* Dont send to the network */
return CMD_LOCALONLY;
}
};
class ModuleLockserv : public Module
{
private:
bool locked;
cmd_lockserv* lockcommand;
cmd_unlockserv* unlockcommand;
virtual void ResetLocked()
{
locked = false;
}
public:
ModuleLockserv(InspIRCd* Me) : Module(Me)
{
ResetLocked();
lockcommand = new cmd_lockserv(ServerInstance, locked);
ServerInstance->AddCommand(lockcommand);
unlockcommand = new cmd_unlockserv(ServerInstance, locked);
ServerInstance->AddCommand(unlockcommand);
}
virtual ~ModuleLockserv()
{
}
void Implements(char* List)
{
List[I_OnUserRegister] = List[I_OnRehash] = List[I_OnCheckReady] = 1;
}
virtual void OnRehash(userrec* user, const std::string ¶meter)
{
ResetLocked();
}
virtual int OnUserRegister(userrec* user)
{
if (locked)
{
userrec::QuitUser(ServerInstance, user, "Server is temporarily closed. Please try again later.");
return 1;
}
return 0;
}
virtual bool OnCheckReady(userrec* user)
{
return !locked;
}
virtual Version GetVersion()
{
return Version(1, 0, 0, 1, VF_VENDOR, API_VERSION);
}
};
MODULE_INIT(ModuleLockserv)
\ No newline at end of file +/* +------------------------------------+ + * | 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 "users.h" +#include "channels.h" +#include "modules.h" + +/* $ModDesc: Allows locking of the server to stop all incoming connections till unlocked again */ + +/** Adds numerics + * 988 <nick> <servername> :Closed for new connections + * 989 <nick> <servername> :Open for new connections +*/ + + +class cmd_lockserv : public command_t +{ +private: + bool& locked; + +public: + cmd_lockserv (InspIRCd* Instance, bool &lock) + : command_t(Instance, "LOCKSERV", 'o', 0), locked(lock) + { + this->source = "m_lockserv.so"; + syntax.clear(); + } + + CmdResult Handle (const char** parameters, int pcnt, userrec *user) + { + locked = true; + user->WriteServ("988 %s %s :Closed for new connections", user->nick, user->server); + ServerInstance->WriteOpers("*** Oper %s used LOCKSERV to temporarily close for new connections", user->nick); + /* Dont send to the network */ + return CMD_LOCALONLY; + } +}; + +class cmd_unlockserv : public command_t +{ +private: + bool& locked; + +public: + cmd_unlockserv (InspIRCd* Instance, bool &lock) + : command_t(Instance, "UNLOCKSERV", 'o', 0), locked(lock) + { + this->source = "m_lockserv.so"; + syntax.clear(); + } + + CmdResult Handle (const char** parameters, int pcnt, userrec *user) + { + locked = false; + user->WriteServ("989 %s %s :Open for new connections", user->nick, user->server); + ServerInstance->WriteOpers("*** Oper %s used UNLOCKSERV to allow for new connections", user->nick); + /* Dont send to the network */ + return CMD_LOCALONLY; + } +}; + +class ModuleLockserv : public Module +{ +private: + bool locked; + cmd_lockserv* lockcommand; + cmd_unlockserv* unlockcommand; + + virtual void ResetLocked() + { + locked = false; + } + +public: + ModuleLockserv(InspIRCd* Me) : Module(Me) + { + ResetLocked(); + lockcommand = new cmd_lockserv(ServerInstance, locked); + ServerInstance->AddCommand(lockcommand); + + unlockcommand = new cmd_unlockserv(ServerInstance, locked); + ServerInstance->AddCommand(unlockcommand); + } + + virtual ~ModuleLockserv() + { + } + + void Implements(char* List) + { + List[I_OnUserRegister] = List[I_OnRehash] = List[I_OnCheckReady] = 1; + } + + virtual void OnRehash(userrec* user, const std::string ¶meter) + { + ResetLocked(); + } + + virtual int OnUserRegister(userrec* user) + { + if (locked) + { + userrec::QuitUser(ServerInstance, user, "Server is temporarily closed. Please try again later."); + return 1; + } + return 0; + } + + virtual bool OnCheckReady(userrec* user) + { + return !locked; + } + + virtual Version GetVersion() + { + return Version(1, 0, 0, 1, VF_VENDOR, API_VERSION); + } +}; + +MODULE_INIT(ModuleLockserv) diff --git a/src/modules/m_md5.cpp b/src/modules/m_md5.cpp index c9b062e43..3b7df8369 100644 --- a/src/modules/m_md5.cpp +++ b/src/modules/m_md5.cpp @@ -1 +1,322 @@ -/* +------------------------------------+
* | 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.
*
* ---------------------------------------------------
*/
/* $ModDesc: Allows for MD5 encrypted oper passwords */
/* $ModDep: m_hash.h */
#include "inspircd.h"
#ifdef HAS_STDINT
#include <stdint.h>
#endif
#include "users.h"
#include "channels.h"
#include "modules.h"
#include "m_hash.h"
/* The four core functions - F1 is optimized somewhat */
#define F1(x, y, z) (z ^ (x & (y ^ z)))
#define F2(x, y, z) F1(z, x, y)
#define F3(x, y, z) (x ^ y ^ z)
#define F4(x, y, z) (y ^ (x | ~z))
/* This is the central step in the MD5 algorithm. */
#define MD5STEP(f,w,x,y,z,in,s) \
(w += f(x,y,z) + in, w = (w<<s | w>>(32-s)) + x)
#ifndef HAS_STDINT
typedef unsigned int uint32_t;
#endif
typedef uint32_t word32; /* NOT unsigned long. We don't support 16 bit platforms, anyway. */
typedef unsigned char byte;
/** An MD5 context, used by m_opermd5
*/
class MD5Context : public classbase
{
public:
word32 buf[4];
word32 bytes[2];
word32 in[16];
};
class ModuleMD5 : public Module
{
void byteSwap(word32 *buf, unsigned words)
{
byte *p = (byte *)buf;
do
{
*buf++ = (word32)((unsigned)p[3] << 8 | p[2]) << 16 |
((unsigned)p[1] << 8 | p[0]);
p += 4;
} while (--words);
}
void MD5Init(MD5Context *ctx, unsigned int* key = NULL)
{
/* These are the defaults for md5 */
if (!key)
{
ctx->buf[0] = 0x67452301;
ctx->buf[1] = 0xefcdab89;
ctx->buf[2] = 0x98badcfe;
ctx->buf[3] = 0x10325476;
}
else
{
ctx->buf[0] = key[0];
ctx->buf[1] = key[1];
ctx->buf[2] = key[2];
ctx->buf[3] = key[3];
}
ctx->bytes[0] = 0;
ctx->bytes[1] = 0;
}
void MD5Update(MD5Context *ctx, byte const *buf, int len)
{
word32 t;
/* Update byte count */
t = ctx->bytes[0];
if ((ctx->bytes[0] = t + len) < t)
ctx->bytes[1]++; /* Carry from low to high */
t = 64 - (t & 0x3f); /* Space available in ctx->in (at least 1) */
if ((unsigned)t > (unsigned)len)
{
memcpy((byte *)ctx->in + 64 - (unsigned)t, buf, len);
return;
}
/* First chunk is an odd size */
memcpy((byte *)ctx->in + 64 - (unsigned)t, buf, (unsigned)t);
byteSwap(ctx->in, 16);
MD5Transform(ctx->buf, ctx->in);
buf += (unsigned)t;
len -= (unsigned)t;
/* Process data in 64-byte chunks */
while (len >= 64)
{
memcpy(ctx->in, buf, 64);
byteSwap(ctx->in, 16);
MD5Transform(ctx->buf, ctx->in);
buf += 64;
len -= 64;
}
/* Handle any remaining bytes of data. */
memcpy(ctx->in, buf, len);
}
void MD5Final(byte digest[16], MD5Context *ctx)
{
int count = (int)(ctx->bytes[0] & 0x3f); /* Bytes in ctx->in */
byte *p = (byte *)ctx->in + count; /* First unused byte */
/* Set the first char of padding to 0x80. There is always room. */
*p++ = 0x80;
/* Bytes of padding needed to make 56 bytes (-8..55) */
count = 56 - 1 - count;
if (count < 0)
{ /* Padding forces an extra block */
memset(p, 0, count+8);
byteSwap(ctx->in, 16);
MD5Transform(ctx->buf, ctx->in);
p = (byte *)ctx->in;
count = 56;
}
memset(p, 0, count+8);
byteSwap(ctx->in, 14);
/* Append length in bits and transform */
ctx->in[14] = ctx->bytes[0] << 3;
ctx->in[15] = ctx->bytes[1] << 3 | ctx->bytes[0] >> 29;
MD5Transform(ctx->buf, ctx->in);
byteSwap(ctx->buf, 4);
memcpy(digest, ctx->buf, 16);
memset(ctx, 0, sizeof(ctx));
}
void MD5Transform(word32 buf[4], word32 const in[16])
{
register word32 a, b, c, d;
a = buf[0];
b = buf[1];
c = buf[2];
d = buf[3];
MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7);
MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12);
MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17);
MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22);
MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7);
MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12);
MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17);
MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22);
MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7);
MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12);
MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17);
MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22);
MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7);
MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12);
MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17);
MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22);
MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5);
MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9);
MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14);
MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20);
MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5);
MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9);
MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14);
MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20);
MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5);
MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9);
MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14);
MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20);
MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5);
MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9);
MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14);
MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20);
MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4);
MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11);
MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16);
MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23);
MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4);
MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11);
MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16);
MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23);
MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4);
MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11);
MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16);
MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23);
MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4);
MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11);
MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16);
MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23);
MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6);
MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10);
MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15);
MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21);
MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6);
MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10);
MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15);
MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21);
MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6);
MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10);
MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15);
MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21);
MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6);
MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10);
MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15);
MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21);
buf[0] += a;
buf[1] += b;
buf[2] += c;
buf[3] += d;
}
void MyMD5(void *dest, void *orig, int len, unsigned int* key)
{
MD5Context context;
MD5Init(&context, key);
MD5Update(&context, (const unsigned char*)orig, len);
MD5Final((unsigned char*)dest, &context);
}
void GenHash(const char* src, char* dest, const char* xtab, unsigned int* key)
{
unsigned char bytes[16];
MyMD5((char*)bytes, (void*)src, strlen(src), key);
for (int i = 0; i < 16; i++)
{
*dest++ = xtab[bytes[i] / 16];
*dest++ = xtab[bytes[i] % 16];
}
*dest++ = 0;
}
unsigned int *key;
char* chars;
public:
ModuleMD5(InspIRCd* Me)
: Module(Me), key(NULL), chars(NULL)
{
ServerInstance->PublishInterface("HashRequest", this);
}
virtual ~ModuleMD5()
{
ServerInstance->UnpublishInterface("HashRequest", this);
}
void Implements(char* List)
{
List[I_OnRequest] = 1;
}
virtual char* OnRequest(Request* request)
{
HashRequest* MD5 = (HashRequest*)request;
if (strcmp("KEY", request->GetId()) == 0)
{
this->key = (unsigned int*)MD5->GetKeyData();
}
else if (strcmp("HEX", request->GetId()) == 0)
{
this->chars = (char*)MD5->GetOutputs();
}
else if (strcmp("SUM", request->GetId()) == 0)
{
static char data[MAXBUF];
GenHash((const char*)MD5->GetHashData(), data, chars ? chars : "0123456789abcdef", key);
return data;
}
else if (strcmp("NAME", request->GetId()) == 0)
{
return "md5";
}
else if (strcmp("RESET", request->GetId()) == 0)
{
this->chars = NULL;
this->key = NULL;
}
return NULL;
}
virtual Version GetVersion()
{
return Version(1,1,0,1,VF_VENDOR|VF_SERVICEPROVIDER,API_VERSION);
}
};
MODULE_INIT(ModuleMD5)
\ No newline at end of file +/* +------------------------------------+ + * | 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. + * + * --------------------------------------------------- + */ + +/* $ModDesc: Allows for MD5 encrypted oper passwords */ +/* $ModDep: m_hash.h */ + +#include "inspircd.h" +#ifdef HAS_STDINT +#include <stdint.h> +#endif +#include "users.h" +#include "channels.h" +#include "modules.h" +#include "m_hash.h" + +/* The four core functions - F1 is optimized somewhat */ +#define F1(x, y, z) (z ^ (x & (y ^ z))) +#define F2(x, y, z) F1(z, x, y) +#define F3(x, y, z) (x ^ y ^ z) +#define F4(x, y, z) (y ^ (x | ~z)) + +/* This is the central step in the MD5 algorithm. */ +#define MD5STEP(f,w,x,y,z,in,s) \ + (w += f(x,y,z) + in, w = (w<<s | w>>(32-s)) + x) + +#ifndef HAS_STDINT +typedef unsigned int uint32_t; +#endif + +typedef uint32_t word32; /* NOT unsigned long. We don't support 16 bit platforms, anyway. */ +typedef unsigned char byte; + +/** An MD5 context, used by m_opermd5 + */ +class MD5Context : public classbase +{ + public: + word32 buf[4]; + word32 bytes[2]; + word32 in[16]; +}; + +class ModuleMD5 : public Module +{ + void byteSwap(word32 *buf, unsigned words) + { + byte *p = (byte *)buf; + + do + { + *buf++ = (word32)((unsigned)p[3] << 8 | p[2]) << 16 | + ((unsigned)p[1] << 8 | p[0]); + p += 4; + } while (--words); + } + + void MD5Init(MD5Context *ctx, unsigned int* key = NULL) + { + /* These are the defaults for md5 */ + if (!key) + { + ctx->buf[0] = 0x67452301; + ctx->buf[1] = 0xefcdab89; + ctx->buf[2] = 0x98badcfe; + ctx->buf[3] = 0x10325476; + } + else + { + ctx->buf[0] = key[0]; + ctx->buf[1] = key[1]; + ctx->buf[2] = key[2]; + ctx->buf[3] = key[3]; + } + + ctx->bytes[0] = 0; + ctx->bytes[1] = 0; + } + + void MD5Update(MD5Context *ctx, byte const *buf, int len) + { + word32 t; + + /* Update byte count */ + + t = ctx->bytes[0]; + if ((ctx->bytes[0] = t + len) < t) + ctx->bytes[1]++; /* Carry from low to high */ + + t = 64 - (t & 0x3f); /* Space available in ctx->in (at least 1) */ + if ((unsigned)t > (unsigned)len) + { + memcpy((byte *)ctx->in + 64 - (unsigned)t, buf, len); + return; + } + /* First chunk is an odd size */ + memcpy((byte *)ctx->in + 64 - (unsigned)t, buf, (unsigned)t); + byteSwap(ctx->in, 16); + MD5Transform(ctx->buf, ctx->in); + buf += (unsigned)t; + len -= (unsigned)t; + + /* Process data in 64-byte chunks */ + while (len >= 64) + { + memcpy(ctx->in, buf, 64); + byteSwap(ctx->in, 16); + MD5Transform(ctx->buf, ctx->in); + buf += 64; + len -= 64; + } + + /* Handle any remaining bytes of data. */ + memcpy(ctx->in, buf, len); + } + + void MD5Final(byte digest[16], MD5Context *ctx) + { + int count = (int)(ctx->bytes[0] & 0x3f); /* Bytes in ctx->in */ + byte *p = (byte *)ctx->in + count; /* First unused byte */ + + /* Set the first char of padding to 0x80. There is always room. */ + *p++ = 0x80; + + /* Bytes of padding needed to make 56 bytes (-8..55) */ + count = 56 - 1 - count; + + if (count < 0) + { /* Padding forces an extra block */ + memset(p, 0, count+8); + byteSwap(ctx->in, 16); + MD5Transform(ctx->buf, ctx->in); + p = (byte *)ctx->in; + count = 56; + } + memset(p, 0, count+8); + byteSwap(ctx->in, 14); + + /* Append length in bits and transform */ + ctx->in[14] = ctx->bytes[0] << 3; + ctx->in[15] = ctx->bytes[1] << 3 | ctx->bytes[0] >> 29; + MD5Transform(ctx->buf, ctx->in); + + byteSwap(ctx->buf, 4); + memcpy(digest, ctx->buf, 16); + memset(ctx, 0, sizeof(ctx)); + } + + void MD5Transform(word32 buf[4], word32 const in[16]) + { + register word32 a, b, c, d; + + a = buf[0]; + b = buf[1]; + c = buf[2]; + d = buf[3]; + + MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7); + MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12); + MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17); + MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22); + MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7); + MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12); + MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17); + MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22); + MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7); + MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12); + MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17); + MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22); + MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7); + MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12); + MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17); + MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22); + + MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5); + MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9); + MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14); + MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20); + MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5); + MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9); + MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14); + MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20); + MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5); + MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9); + MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14); + MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20); + MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5); + MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9); + MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14); + MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20); + + MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4); + MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11); + MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16); + MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23); + MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4); + MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11); + MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16); + MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23); + MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4); + MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11); + MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16); + MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23); + MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4); + MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11); + MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16); + MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23); + + MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6); + MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10); + MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15); + MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21); + MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6); + MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10); + MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15); + MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21); + MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6); + MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10); + MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15); + MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21); + MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6); + MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10); + MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15); + MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21); + + buf[0] += a; + buf[1] += b; + buf[2] += c; + buf[3] += d; + } + + + void MyMD5(void *dest, void *orig, int len, unsigned int* key) + { + MD5Context context; + MD5Init(&context, key); + MD5Update(&context, (const unsigned char*)orig, len); + MD5Final((unsigned char*)dest, &context); + } + + + void GenHash(const char* src, char* dest, const char* xtab, unsigned int* key) + { + unsigned char bytes[16]; + + MyMD5((char*)bytes, (void*)src, strlen(src), key); + + for (int i = 0; i < 16; i++) + { + *dest++ = xtab[bytes[i] / 16]; + *dest++ = xtab[bytes[i] % 16]; + } + *dest++ = 0; + } + + unsigned int *key; + char* chars; + + public: + + ModuleMD5(InspIRCd* Me) + : Module(Me), key(NULL), chars(NULL) + { + ServerInstance->PublishInterface("HashRequest", this); + } + + virtual ~ModuleMD5() + { + ServerInstance->UnpublishInterface("HashRequest", this); + } + + void Implements(char* List) + { + List[I_OnRequest] = 1; + } + + virtual char* OnRequest(Request* request) + { + HashRequest* MD5 = (HashRequest*)request; + + if (strcmp("KEY", request->GetId()) == 0) + { + this->key = (unsigned int*)MD5->GetKeyData(); + } + else if (strcmp("HEX", request->GetId()) == 0) + { + this->chars = (char*)MD5->GetOutputs(); + } + else if (strcmp("SUM", request->GetId()) == 0) + { + static char data[MAXBUF]; + GenHash((const char*)MD5->GetHashData(), data, chars ? chars : "0123456789abcdef", key); + return data; + } + else if (strcmp("NAME", request->GetId()) == 0) + { + return "md5"; + } + else if (strcmp("RESET", request->GetId()) == 0) + { + this->chars = NULL; + this->key = NULL; + } + return NULL; + } + + virtual Version GetVersion() + { + return Version(1,1,0,1,VF_VENDOR|VF_SERVICEPROVIDER,API_VERSION); + } +}; + +MODULE_INIT(ModuleMD5) diff --git a/src/modules/m_messageflood.cpp b/src/modules/m_messageflood.cpp index e5263719b..a942262ed 100644 --- a/src/modules/m_messageflood.cpp +++ b/src/modules/m_messageflood.cpp @@ -1 +1,304 @@ -/* +------------------------------------+
* | 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 "users.h"
#include "channels.h"
#include "modules.h"
/* $ModDesc: Provides channel mode +f (message flood protection) */
/** Holds flood settings and state for mode +f
*/
class floodsettings : public classbase
{
public:
bool ban;
int secs;
int lines;
time_t reset;
std::map<userrec*,int> counters;
floodsettings() : ban(0), secs(0), lines(0) {};
floodsettings(bool a, int b, int c) : ban(a), secs(b), lines(c)
{
reset = time(NULL) + secs;
};
void addmessage(userrec* who)
{
std::map<userrec*,int>::iterator iter = counters.find(who);
if (iter != counters.end())
{
iter->second++;
}
else
{
counters[who] = 1;
}
if (time(NULL) > reset)
{
counters.clear();
reset = time(NULL) + secs;
}
}
bool shouldkick(userrec* who)
{
std::map<userrec*,int>::iterator iter = counters.find(who);
if (iter != counters.end())
{
return (iter->second >= this->lines);
}
else return false;
}
void clear(userrec* who)
{
std::map<userrec*,int>::iterator iter = counters.find(who);
if (iter != counters.end())
{
counters.erase(iter);
}
}
};
/** Handles channel mode +f
*/
class MsgFlood : public ModeHandler
{
public:
MsgFlood(InspIRCd* Instance) : ModeHandler(Instance, 'f', 1, 0, false, MODETYPE_CHANNEL, false) { }
ModePair ModeSet(userrec* source, userrec* dest, chanrec* channel, const std::string ¶meter)
{
floodsettings* x;
if (channel->GetExt("flood",x))
return std::make_pair(true, (x->ban ? "*" : "")+ConvToStr(x->lines)+":"+ConvToStr(x->secs));
else
return std::make_pair(false, parameter);
}
bool CheckTimeStamp(time_t theirs, time_t ours, const std::string &their_param, const std::string &our_param, chanrec* channel)
{
/* When TS is equal, the alphabetically later one wins */
return (their_param < our_param);
}
ModeAction OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string ¶meter, bool adding)
{
floodsettings *f;
if (adding)
{
char ndata[MAXBUF];
char* data = ndata;
strlcpy(ndata,parameter.c_str(),MAXBUF);
char* lines = data;
char* secs = NULL;
bool ban = false;
if (*data == '*')
{
ban = true;
lines++;
}
else
{
ban = false;
}
while (*data)
{
if (*data == ':')
{
*data = 0;
data++;
secs = data;
break;
}
else data++;
}
if (secs)
{
/* Set up the flood parameters for this channel */
int nlines = atoi(lines);
int nsecs = atoi(secs);
if ((nlines<1) || (nsecs<1))
{
source->WriteServ("608 %s %s :Invalid flood parameter",source->nick,channel->name);
parameter.clear();
return MODEACTION_DENY;
}
else
{
if (!channel->GetExt("flood", f))
{
parameter = std::string(ban ? "*" : "") + ConvToStr(nlines) + ":" +ConvToStr(nsecs);
floodsettings *f = new floodsettings(ban,nsecs,nlines);
channel->Extend("flood",f);
channel->SetMode('f', true);
channel->SetModeParam('f', parameter.c_str(), true);
return MODEACTION_ALLOW;
}
else
{
std::string cur_param = channel->GetModeParameter('f');
parameter = std::string(ban ? "*" : "") + ConvToStr(nlines) + ":" +ConvToStr(nsecs);
if (cur_param == parameter)
{
// mode params match
return MODEACTION_DENY;
}
else
{
if (((nlines != f->lines) || (nsecs != f->secs)) && ((nsecs > 0) && (nlines > 0)) || (ban != f->ban))
{
delete f;
floodsettings *f = new floodsettings(ban,nsecs,nlines);
channel->Shrink("flood");
channel->Extend("flood",f);
channel->SetModeParam('f', cur_param.c_str(), false);
channel->SetModeParam('f', parameter.c_str(), true);
return MODEACTION_ALLOW;
}
else
{
return MODEACTION_DENY;
}
}
}
}
}
else
{
source->WriteServ("608 %s %s :Invalid flood parameter",source->nick,channel->name);
parameter.clear();
return MODEACTION_DENY;
}
}
else
{
if (channel->GetExt("flood", f))
{
DELETE(f);
channel->Shrink("flood");
channel->SetMode('f', false);
return MODEACTION_ALLOW;
}
}
return MODEACTION_DENY;
}
};
class ModuleMsgFlood : public Module
{
MsgFlood* mf;
public:
ModuleMsgFlood(InspIRCd* Me)
: Module(Me)
{
mf = new MsgFlood(ServerInstance);
if (!ServerInstance->AddMode(mf, 'f'))
throw ModuleException("Could not add new modes!");
}
void ProcessMessages(userrec* user,chanrec* dest, const std::string &text)
{
if (!IS_LOCAL(user) || CHANOPS_EXEMPT(ServerInstance, 'f') && dest->GetStatus(user) == STATUS_OP)
{
return;
}
floodsettings *f;
if (dest->GetExt("flood", f))
{
f->addmessage(user);
if (f->shouldkick(user))
{
/* Youre outttta here! */
f->clear(user);
if (f->ban)
{
const char* parameters[3];
parameters[0] = dest->name;
parameters[1] = "+b";
parameters[2] = user->MakeWildHost();
ServerInstance->SendMode(parameters,3,user);
std::deque<std::string> n;
/* Propogate the ban to other servers.
* We dont know what protocol we may be using,
* so this event is picked up by our protocol
* module and formed into a ban command that
* suits the protocol in use.
*/
n.push_back(dest->name);
n.push_back("+b");
n.push_back(user->MakeWildHost());
Event rmode((char *)&n, NULL, "send_mode");
rmode.Send(ServerInstance);
}
char kickmessage[MAXBUF];
snprintf(kickmessage, MAXBUF, "Channel flood triggered (limit is %d lines in %d secs)", f->lines, f->secs);
dest->ServerKickUser(user, kickmessage, true);
}
}
}
virtual void OnUserMessage(userrec* user, void* dest, int target_type, const std::string &text, char status, const CUList &exempt_list)
{
if (target_type == TYPE_CHANNEL)
{
ProcessMessages(user,(chanrec*)dest,text);
}
}
virtual void OnUserNotice(userrec* user, void* dest, int target_type, const std::string &text, char status, const CUList &exempt_list)
{
if (target_type == TYPE_CHANNEL)
{
ProcessMessages(user,(chanrec*)dest,text);
}
}
void OnChannelDelete(chanrec* chan)
{
floodsettings* f;
if (chan->GetExt("flood", f))
{
DELETE(f);
chan->Shrink("flood");
}
}
void Implements(char* List)
{
List[I_OnChannelDelete] = List[I_OnUserNotice] = List[I_OnUserMessage] = 1;
}
virtual ~ModuleMsgFlood()
{
ServerInstance->Modes->DelMode(mf);
DELETE(mf);
}
virtual Version GetVersion()
{
return Version(1, 1, 0, 0, VF_COMMON | VF_VENDOR, API_VERSION);
}
};
MODULE_INIT(ModuleMsgFlood)
\ No newline at end of file +/* +------------------------------------+ + * | 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 "users.h" +#include "channels.h" +#include "modules.h" + +/* $ModDesc: Provides channel mode +f (message flood protection) */ + +/** Holds flood settings and state for mode +f + */ +class floodsettings : public classbase +{ + public: + bool ban; + int secs; + int lines; + time_t reset; + std::map<userrec*,int> counters; + + floodsettings() : ban(0), secs(0), lines(0) {}; + floodsettings(bool a, int b, int c) : ban(a), secs(b), lines(c) + { + reset = time(NULL) + secs; + }; + + void addmessage(userrec* who) + { + std::map<userrec*,int>::iterator iter = counters.find(who); + if (iter != counters.end()) + { + iter->second++; + } + else + { + counters[who] = 1; + } + if (time(NULL) > reset) + { + counters.clear(); + reset = time(NULL) + secs; + } + } + + bool shouldkick(userrec* who) + { + std::map<userrec*,int>::iterator iter = counters.find(who); + if (iter != counters.end()) + { + return (iter->second >= this->lines); + } + else return false; + } + + void clear(userrec* who) + { + std::map<userrec*,int>::iterator iter = counters.find(who); + if (iter != counters.end()) + { + counters.erase(iter); + } + } +}; + +/** Handles channel mode +f + */ +class MsgFlood : public ModeHandler +{ + public: + MsgFlood(InspIRCd* Instance) : ModeHandler(Instance, 'f', 1, 0, false, MODETYPE_CHANNEL, false) { } + + ModePair ModeSet(userrec* source, userrec* dest, chanrec* channel, const std::string ¶meter) + { + floodsettings* x; + if (channel->GetExt("flood",x)) + return std::make_pair(true, (x->ban ? "*" : "")+ConvToStr(x->lines)+":"+ConvToStr(x->secs)); + else + return std::make_pair(false, parameter); + } + + bool CheckTimeStamp(time_t theirs, time_t ours, const std::string &their_param, const std::string &our_param, chanrec* channel) + { + /* When TS is equal, the alphabetically later one wins */ + return (their_param < our_param); + } + + ModeAction OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string ¶meter, bool adding) + { + floodsettings *f; + + if (adding) + { + char ndata[MAXBUF]; + char* data = ndata; + strlcpy(ndata,parameter.c_str(),MAXBUF); + char* lines = data; + char* secs = NULL; + bool ban = false; + if (*data == '*') + { + ban = true; + lines++; + } + else + { + ban = false; + } + while (*data) + { + if (*data == ':') + { + *data = 0; + data++; + secs = data; + break; + } + else data++; + } + if (secs) + { + /* Set up the flood parameters for this channel */ + int nlines = atoi(lines); + int nsecs = atoi(secs); + if ((nlines<1) || (nsecs<1)) + { + source->WriteServ("608 %s %s :Invalid flood parameter",source->nick,channel->name); + parameter.clear(); + return MODEACTION_DENY; + } + else + { + if (!channel->GetExt("flood", f)) + { + parameter = std::string(ban ? "*" : "") + ConvToStr(nlines) + ":" +ConvToStr(nsecs); + floodsettings *f = new floodsettings(ban,nsecs,nlines); + channel->Extend("flood",f); + channel->SetMode('f', true); + channel->SetModeParam('f', parameter.c_str(), true); + return MODEACTION_ALLOW; + } + else + { + std::string cur_param = channel->GetModeParameter('f'); + parameter = std::string(ban ? "*" : "") + ConvToStr(nlines) + ":" +ConvToStr(nsecs); + if (cur_param == parameter) + { + // mode params match + return MODEACTION_DENY; + } + else + { + if (((nlines != f->lines) || (nsecs != f->secs)) && ((nsecs > 0) && (nlines > 0)) || (ban != f->ban)) + { + delete f; + floodsettings *f = new floodsettings(ban,nsecs,nlines); + channel->Shrink("flood"); + channel->Extend("flood",f); + channel->SetModeParam('f', cur_param.c_str(), false); + channel->SetModeParam('f', parameter.c_str(), true); + return MODEACTION_ALLOW; + } + else + { + return MODEACTION_DENY; + } + } + } + } + } + else + { + source->WriteServ("608 %s %s :Invalid flood parameter",source->nick,channel->name); + parameter.clear(); + return MODEACTION_DENY; + } + } + else + { + if (channel->GetExt("flood", f)) + { + DELETE(f); + channel->Shrink("flood"); + channel->SetMode('f', false); + return MODEACTION_ALLOW; + } + } + + return MODEACTION_DENY; + } +}; + +class ModuleMsgFlood : public Module +{ + + MsgFlood* mf; + + public: + + ModuleMsgFlood(InspIRCd* Me) + : Module(Me) + { + + mf = new MsgFlood(ServerInstance); + if (!ServerInstance->AddMode(mf, 'f')) + throw ModuleException("Could not add new modes!"); + } + + void ProcessMessages(userrec* user,chanrec* dest, const std::string &text) + { + if (!IS_LOCAL(user) || CHANOPS_EXEMPT(ServerInstance, 'f') && dest->GetStatus(user) == STATUS_OP) + { + return; + } + + floodsettings *f; + if (dest->GetExt("flood", f)) + { + f->addmessage(user); + if (f->shouldkick(user)) + { + /* Youre outttta here! */ + f->clear(user); + if (f->ban) + { + const char* parameters[3]; + parameters[0] = dest->name; + parameters[1] = "+b"; + parameters[2] = user->MakeWildHost(); + ServerInstance->SendMode(parameters,3,user); + std::deque<std::string> n; + /* Propogate the ban to other servers. + * We dont know what protocol we may be using, + * so this event is picked up by our protocol + * module and formed into a ban command that + * suits the protocol in use. + */ + n.push_back(dest->name); + n.push_back("+b"); + n.push_back(user->MakeWildHost()); + Event rmode((char *)&n, NULL, "send_mode"); + rmode.Send(ServerInstance); + } + char kickmessage[MAXBUF]; + snprintf(kickmessage, MAXBUF, "Channel flood triggered (limit is %d lines in %d secs)", f->lines, f->secs); + dest->ServerKickUser(user, kickmessage, true); + } + } + } + + virtual void OnUserMessage(userrec* user, void* dest, int target_type, const std::string &text, char status, const CUList &exempt_list) + { + if (target_type == TYPE_CHANNEL) + { + ProcessMessages(user,(chanrec*)dest,text); + } + } + + virtual void OnUserNotice(userrec* user, void* dest, int target_type, const std::string &text, char status, const CUList &exempt_list) + { + if (target_type == TYPE_CHANNEL) + { + ProcessMessages(user,(chanrec*)dest,text); + } + } + + void OnChannelDelete(chanrec* chan) + { + floodsettings* f; + if (chan->GetExt("flood", f)) + { + DELETE(f); + chan->Shrink("flood"); + } + } + + void Implements(char* List) + { + List[I_OnChannelDelete] = List[I_OnUserNotice] = List[I_OnUserMessage] = 1; + } + + virtual ~ModuleMsgFlood() + { + ServerInstance->Modes->DelMode(mf); + DELETE(mf); + } + + virtual Version GetVersion() + { + return Version(1, 1, 0, 0, VF_COMMON | VF_VENDOR, API_VERSION); + } +}; + +MODULE_INIT(ModuleMsgFlood) diff --git a/src/modules/m_namesx.cpp b/src/modules/m_namesx.cpp index 37f584331..c45d777f8 100644 --- a/src/modules/m_namesx.cpp +++ b/src/modules/m_namesx.cpp @@ -1 +1,127 @@ -/* +------------------------------------+
* | 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 "users.h"
#include "channels.h"
#include "modules.h"
static const char* dummy = "ON";
/* $ModDesc: Provides aliases of commands. */
class ModuleNamesX : public Module
{
public:
ModuleNamesX(InspIRCd* Me)
: Module(Me)
{
}
void Implements(char* List)
{
List[I_OnSyncUserMetaData] = List[I_OnPreCommand] = List[I_OnUserList] = List[I_On005Numeric] = 1;
}
virtual ~ModuleNamesX()
{
}
void OnSyncUserMetaData(userrec* user, Module* proto,void* opaque, const std::string &extname, bool displayable)
{
if ((displayable) && (extname == "NAMESX"))
proto->ProtoSendMetaData(opaque, TYPE_USER, user, extname, "Enabled");
}
virtual Version GetVersion()
{
return Version(1,1,0,1,VF_VENDOR,API_VERSION);
}
virtual void On005Numeric(std::string &output)
{
output.append(" NAMESX");
}
virtual int OnPreCommand(const std::string &command, const char** parameters, int pcnt, userrec *user, bool validated, const std::string &original_line)
{
irc::string c = command.c_str();
/* We don't actually create a proper command handler class for PROTOCTL,
* because other modules might want to have PROTOCTL hooks too.
* Therefore, we just hook its as an unvalidated command therefore we
* can capture it even if it doesnt exist! :-)
*/
if (c == "PROTOCTL")
{
if ((pcnt) && (!strcasecmp(parameters[0],"NAMESX")))
{
user->Extend("NAMESX",dummy);
return 1;
}
}
return 0;
}
virtual int OnUserList(userrec* user, chanrec* Ptr, CUList* &ulist)
{
if (user->GetExt("NAMESX"))
{
char list[MAXBUF];
size_t dlen, curlen;
dlen = curlen = snprintf(list,MAXBUF,"353 %s = %s :", user->nick, Ptr->name);
int numusers = 0;
char* ptr = list + dlen;
if (!ulist)
ulist = Ptr->GetUsers();
bool has_user = Ptr->HasUser(user);
for (CUList::iterator i = ulist->begin(); i != ulist->end(); i++)
{
if ((!has_user) && (i->first->IsModeSet('i')))
continue;
if (i->first->Visibility && !i->first->Visibility->VisibleTo(user))
continue;
size_t ptrlen = snprintf(ptr, MAXBUF, "%s%s ", Ptr->GetAllPrefixChars(i->first), i->second.c_str());
/* OnUserList can change this - reset it back to normal */
i->second = i->first->nick;
curlen += ptrlen;
ptr += ptrlen;
numusers++;
if (curlen > (480-NICKMAX))
{
/* list overflowed into multiple numerics */
user->WriteServ(std::string(list));
/* reset our lengths */
dlen = curlen = snprintf(list,MAXBUF,"353 %s = %s :", user->nick, Ptr->name);
ptr = list + dlen;
ptrlen = 0;
numusers = 0;
}
}
/* if whats left in the list isnt empty, send it */
if (numusers)
{
user->WriteServ(std::string(list));
}
user->WriteServ("366 %s %s :End of /NAMES list.", user->nick, Ptr->name);
return 1;
}
return 0;
}
};
MODULE_INIT(ModuleNamesX)
\ No newline at end of file +/* +------------------------------------+ + * | 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 "users.h" +#include "channels.h" +#include "modules.h" + +static const char* dummy = "ON"; + +/* $ModDesc: Provides aliases of commands. */ + +class ModuleNamesX : public Module +{ + public: + + ModuleNamesX(InspIRCd* Me) + : Module(Me) + { + } + + void Implements(char* List) + { + List[I_OnSyncUserMetaData] = List[I_OnPreCommand] = List[I_OnUserList] = List[I_On005Numeric] = 1; + } + + virtual ~ModuleNamesX() + { + } + + void OnSyncUserMetaData(userrec* user, Module* proto,void* opaque, const std::string &extname, bool displayable) + { + if ((displayable) && (extname == "NAMESX")) + proto->ProtoSendMetaData(opaque, TYPE_USER, user, extname, "Enabled"); + } + + virtual Version GetVersion() + { + return Version(1,1,0,1,VF_VENDOR,API_VERSION); + } + + virtual void On005Numeric(std::string &output) + { + output.append(" NAMESX"); + } + + virtual int OnPreCommand(const std::string &command, const char** parameters, int pcnt, userrec *user, bool validated, const std::string &original_line) + { + irc::string c = command.c_str(); + /* We don't actually create a proper command handler class for PROTOCTL, + * because other modules might want to have PROTOCTL hooks too. + * Therefore, we just hook its as an unvalidated command therefore we + * can capture it even if it doesnt exist! :-) + */ + if (c == "PROTOCTL") + { + if ((pcnt) && (!strcasecmp(parameters[0],"NAMESX"))) + { + user->Extend("NAMESX",dummy); + return 1; + } + } + return 0; + } + + virtual int OnUserList(userrec* user, chanrec* Ptr, CUList* &ulist) + { + if (user->GetExt("NAMESX")) + { + char list[MAXBUF]; + size_t dlen, curlen; + dlen = curlen = snprintf(list,MAXBUF,"353 %s = %s :", user->nick, Ptr->name); + int numusers = 0; + char* ptr = list + dlen; + + if (!ulist) + ulist = Ptr->GetUsers(); + + bool has_user = Ptr->HasUser(user); + for (CUList::iterator i = ulist->begin(); i != ulist->end(); i++) + { + if ((!has_user) && (i->first->IsModeSet('i'))) + continue; + + if (i->first->Visibility && !i->first->Visibility->VisibleTo(user)) + continue; + + size_t ptrlen = snprintf(ptr, MAXBUF, "%s%s ", Ptr->GetAllPrefixChars(i->first), i->second.c_str()); + /* OnUserList can change this - reset it back to normal */ + i->second = i->first->nick; + curlen += ptrlen; + ptr += ptrlen; + numusers++; + if (curlen > (480-NICKMAX)) + { + /* list overflowed into multiple numerics */ + user->WriteServ(std::string(list)); + /* reset our lengths */ + dlen = curlen = snprintf(list,MAXBUF,"353 %s = %s :", user->nick, Ptr->name); + ptr = list + dlen; + ptrlen = 0; + numusers = 0; + } + } + /* if whats left in the list isnt empty, send it */ + if (numusers) + { + user->WriteServ(std::string(list)); + } + user->WriteServ("366 %s %s :End of /NAMES list.", user->nick, Ptr->name); + return 1; + } + return 0; + } +}; + +MODULE_INIT(ModuleNamesX) diff --git a/src/modules/m_nicklock.cpp b/src/modules/m_nicklock.cpp index 94c934e6f..26d5bfbd7 100644 --- a/src/modules/m_nicklock.cpp +++ b/src/modules/m_nicklock.cpp @@ -1 +1,159 @@ -/* +------------------------------------+
* | 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 "users.h"
#include "channels.h"
#include "modules.h"
#include "hashcomp.h"
/* $ModDesc: Provides the NICKLOCK command, allows an oper to chage a users nick and lock them to it until they quit */
/** Handle /NICKLOCK
*/
class cmd_nicklock : public command_t
{
char* dummy;
public:
cmd_nicklock (InspIRCd* Instance) : command_t(Instance,"NICKLOCK", 'o', 2)
{
this->source = "m_nicklock.so";
syntax = "<oldnick> <newnick>";
}
CmdResult Handle(const char** parameters, int pcnt, userrec *user)
{
userrec* source = ServerInstance->FindNick(parameters[0]);
irc::string server;
irc::string me;
// check user exists
if (!source)
{
return CMD_FAILURE;
}
// check if user is locked
if (source->GetExt("nick_locked", dummy))
{
user->WriteServ("946 %s %s :This user's nickname is already locked.",user->nick,source->nick);
return CMD_FAILURE;
}
// check nick is valid
if (!ServerInstance->IsNick(parameters[1]))
{
return CMD_FAILURE;
}
// let others know
ServerInstance->WriteOpers(std::string(user->nick)+" used NICKLOCK to change and hold "+parameters[0]+" to "+parameters[1]);
if (!source->ForceNickChange(parameters[1]))
{
// ugh, nickchange failed for some reason -- possibly existing nick?
userrec::QuitUser(ServerInstance, source, "Nickname collision");
return CMD_FAILURE;
}
// give them a lock flag
source->Extend("nick_locked", "ON");
/* route */
return CMD_SUCCESS;
}
};
/** Handle /NICKUNLOCK
*/
class cmd_nickunlock : public command_t
{
public:
cmd_nickunlock (InspIRCd* Instance) : command_t(Instance,"NICKUNLOCK", 'o', 1)
{
this->source = "m_nickunlock.so";
syntax = "<locked-nick>";
}
CmdResult Handle (const char** parameters, int pcnt, userrec *user)
{
userrec* source = ServerInstance->FindNick(parameters[0]);
if (source)
{
source->Shrink("nick_locked");
user->WriteServ("945 %s %s :Nickname now unlocked.",user->nick,source->nick);
ServerInstance->WriteOpers(std::string(user->nick)+" used NICKUNLOCK on "+parameters[0]);
return CMD_SUCCESS;
}
return CMD_FAILURE;
}
};
class ModuleNickLock : public Module
{
cmd_nicklock* cmd1;
cmd_nickunlock* cmd2;
char* n;
public:
ModuleNickLock(InspIRCd* Me)
: Module(Me)
{
cmd1 = new cmd_nicklock(ServerInstance);
cmd2 = new cmd_nickunlock(ServerInstance);
ServerInstance->AddCommand(cmd1);
ServerInstance->AddCommand(cmd2);
}
virtual ~ModuleNickLock()
{
}
virtual Version GetVersion()
{
return Version(1,1,0,1,VF_VENDOR,API_VERSION);
}
void Implements(char* List)
{
List[I_OnUserPreNick] = List[I_OnUserQuit] = List[I_OnCleanup] = 1;
}
virtual int OnUserPreNick(userrec* user, const std::string &newnick)
{
if (user->GetExt("nick_locked", n))
{
user->WriteServ("447 %s :You cannot change your nickname (your nick is locked)",user->nick);
return 1;
}
return 0;
}
virtual void OnUserQuit(userrec* user, const std::string &reason, const std::string &oper_message)
{
user->Shrink("nick_locked");
}
virtual void OnCleanup(int target_type, void* item)
{
if(target_type == TYPE_USER)
{
userrec* user = (userrec*)item;
user->Shrink("nick_locked");
}
}
};
MODULE_INIT(ModuleNickLock)
\ No newline at end of file +/* +------------------------------------+ + * | 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 "users.h" +#include "channels.h" +#include "modules.h" +#include "hashcomp.h" + +/* $ModDesc: Provides the NICKLOCK command, allows an oper to chage a users nick and lock them to it until they quit */ + +/** Handle /NICKLOCK + */ +class cmd_nicklock : public command_t +{ + char* dummy; + public: + cmd_nicklock (InspIRCd* Instance) : command_t(Instance,"NICKLOCK", 'o', 2) + { + this->source = "m_nicklock.so"; + syntax = "<oldnick> <newnick>"; + } + + CmdResult Handle(const char** parameters, int pcnt, userrec *user) + { + userrec* source = ServerInstance->FindNick(parameters[0]); + irc::string server; + irc::string me; + + // check user exists + if (!source) + { + return CMD_FAILURE; + } + + // check if user is locked + if (source->GetExt("nick_locked", dummy)) + { + user->WriteServ("946 %s %s :This user's nickname is already locked.",user->nick,source->nick); + return CMD_FAILURE; + } + + // check nick is valid + if (!ServerInstance->IsNick(parameters[1])) + { + return CMD_FAILURE; + } + + // let others know + ServerInstance->WriteOpers(std::string(user->nick)+" used NICKLOCK to change and hold "+parameters[0]+" to "+parameters[1]); + + if (!source->ForceNickChange(parameters[1])) + { + // ugh, nickchange failed for some reason -- possibly existing nick? + userrec::QuitUser(ServerInstance, source, "Nickname collision"); + return CMD_FAILURE; + } + + // give them a lock flag + source->Extend("nick_locked", "ON"); + + /* route */ + return CMD_SUCCESS; + } +}; + +/** Handle /NICKUNLOCK + */ +class cmd_nickunlock : public command_t +{ + public: + cmd_nickunlock (InspIRCd* Instance) : command_t(Instance,"NICKUNLOCK", 'o', 1) + { + this->source = "m_nickunlock.so"; + syntax = "<locked-nick>"; + } + + CmdResult Handle (const char** parameters, int pcnt, userrec *user) + { + userrec* source = ServerInstance->FindNick(parameters[0]); + if (source) + { + source->Shrink("nick_locked"); + user->WriteServ("945 %s %s :Nickname now unlocked.",user->nick,source->nick); + ServerInstance->WriteOpers(std::string(user->nick)+" used NICKUNLOCK on "+parameters[0]); + return CMD_SUCCESS; + } + + return CMD_FAILURE; + } +}; + + +class ModuleNickLock : public Module +{ + cmd_nicklock* cmd1; + cmd_nickunlock* cmd2; + char* n; + public: + ModuleNickLock(InspIRCd* Me) + : Module(Me) + { + + cmd1 = new cmd_nicklock(ServerInstance); + cmd2 = new cmd_nickunlock(ServerInstance); + ServerInstance->AddCommand(cmd1); + ServerInstance->AddCommand(cmd2); + } + + virtual ~ModuleNickLock() + { + } + + virtual Version GetVersion() + { + return Version(1,1,0,1,VF_VENDOR,API_VERSION); + } + + void Implements(char* List) + { + List[I_OnUserPreNick] = List[I_OnUserQuit] = List[I_OnCleanup] = 1; + } + + virtual int OnUserPreNick(userrec* user, const std::string &newnick) + { + if (user->GetExt("nick_locked", n)) + { + user->WriteServ("447 %s :You cannot change your nickname (your nick is locked)",user->nick); + return 1; + } + return 0; + } + + virtual void OnUserQuit(userrec* user, const std::string &reason, const std::string &oper_message) + { + user->Shrink("nick_locked"); + } + + virtual void OnCleanup(int target_type, void* item) + { + if(target_type == TYPE_USER) + { + userrec* user = (userrec*)item; + user->Shrink("nick_locked"); + } + } +}; + +MODULE_INIT(ModuleNickLock) diff --git a/src/modules/m_noctcp.cpp b/src/modules/m_noctcp.cpp index b3445155a..05dbd69ca 100644 --- a/src/modules/m_noctcp.cpp +++ b/src/modules/m_noctcp.cpp @@ -1 +1,107 @@ -/* +------------------------------------+
* | 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 "users.h"
#include "channels.h"
#include "modules.h"
/* $ModDesc: Provides support for unreal-style channel mode +c */
class NoCTCP : public ModeHandler
{
public:
NoCTCP(InspIRCd* Instance) : ModeHandler(Instance, 'C', 0, 0, false, MODETYPE_CHANNEL, false) { }
ModeAction OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string ¶meter, bool adding)
{
if (adding)
{
if (!channel->IsModeSet('C'))
{
channel->SetMode('C',true);
return MODEACTION_ALLOW;
}
}
else
{
if (channel->IsModeSet('C'))
{
channel->SetMode('C',false);
return MODEACTION_ALLOW;
}
}
return MODEACTION_DENY;
}
};
class ModuleNoCTCP : public Module
{
NoCTCP* nc;
public:
ModuleNoCTCP(InspIRCd* Me)
: Module(Me)
{
nc = new NoCTCP(ServerInstance);
if (!ServerInstance->AddMode(nc, 'C'))
throw ModuleException("Could not add new modes!");
}
void Implements(char* List)
{
List[I_OnUserPreMessage] = List[I_OnUserPreNotice] = 1;
}
virtual int OnUserPreMessage(userrec* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list)
{
return OnUserPreNotice(user,dest,target_type,text,status,exempt_list);
}
virtual int OnUserPreNotice(userrec* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list)
{
if ((target_type == TYPE_CHANNEL) && (IS_LOCAL(user)))
{
chanrec* c = (chanrec*)dest;
if (c->IsModeSet('C'))
{
if ((text.length()) && (text[0] == '\1'))
{
if (strncmp(text.c_str(),"\1ACTION ",8))
{
user->WriteServ("492 %s %s :Can't send CTCP to channel (+C set)",user->nick, c->name);
return 1;
}
}
}
}
return 0;
}
virtual ~ModuleNoCTCP()
{
ServerInstance->Modes->DelMode(nc);
DELETE(nc);
}
virtual Version GetVersion()
{
return Version(1,1,0,0,VF_COMMON|VF_VENDOR,API_VERSION);
}
};
MODULE_INIT(ModuleNoCTCP)
\ No newline at end of file +/* +------------------------------------+ + * | 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 "users.h" +#include "channels.h" +#include "modules.h" + +/* $ModDesc: Provides support for unreal-style channel mode +c */ + +class NoCTCP : public ModeHandler +{ + public: + NoCTCP(InspIRCd* Instance) : ModeHandler(Instance, 'C', 0, 0, false, MODETYPE_CHANNEL, false) { } + + ModeAction OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string ¶meter, bool adding) + { + if (adding) + { + if (!channel->IsModeSet('C')) + { + channel->SetMode('C',true); + return MODEACTION_ALLOW; + } + } + else + { + if (channel->IsModeSet('C')) + { + channel->SetMode('C',false); + return MODEACTION_ALLOW; + } + } + + return MODEACTION_DENY; + } +}; + +class ModuleNoCTCP : public Module +{ + + NoCTCP* nc; + + public: + + ModuleNoCTCP(InspIRCd* Me) + : Module(Me) + { + + nc = new NoCTCP(ServerInstance); + if (!ServerInstance->AddMode(nc, 'C')) + throw ModuleException("Could not add new modes!"); + } + + void Implements(char* List) + { + List[I_OnUserPreMessage] = List[I_OnUserPreNotice] = 1; + } + + virtual int OnUserPreMessage(userrec* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list) + { + return OnUserPreNotice(user,dest,target_type,text,status,exempt_list); + } + + virtual int OnUserPreNotice(userrec* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list) + { + if ((target_type == TYPE_CHANNEL) && (IS_LOCAL(user))) + { + chanrec* c = (chanrec*)dest; + if (c->IsModeSet('C')) + { + if ((text.length()) && (text[0] == '\1')) + { + if (strncmp(text.c_str(),"\1ACTION ",8)) + { + user->WriteServ("492 %s %s :Can't send CTCP to channel (+C set)",user->nick, c->name); + return 1; + } + } + } + } + return 0; + } + + virtual ~ModuleNoCTCP() + { + ServerInstance->Modes->DelMode(nc); + DELETE(nc); + } + + virtual Version GetVersion() + { + return Version(1,1,0,0,VF_COMMON|VF_VENDOR,API_VERSION); + } +}; + +MODULE_INIT(ModuleNoCTCP) diff --git a/src/modules/m_noinvite.cpp b/src/modules/m_noinvite.cpp index 76e2616e3..26965d319 100644 --- a/src/modules/m_noinvite.cpp +++ b/src/modules/m_noinvite.cpp @@ -1 +1,88 @@ -/* +------------------------------------+
* | 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 "users.h"
#include "channels.h"
#include "modules.h"
/* $ModDesc: Provides support for unreal-style channel mode +V */
class NoInvite : public ModeHandler
{
public:
NoInvite(InspIRCd* Instance) : ModeHandler(Instance, 'V', 0, 0, false, MODETYPE_CHANNEL, false) { }
ModeAction OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string ¶meter, bool adding)
{
if (adding)
{
if (!channel->IsModeSet('V'))
{
channel->SetMode('V',true);
return MODEACTION_ALLOW;
}
}
else
{
if (channel->IsModeSet('V'))
{
channel->SetMode('V',false);
return MODEACTION_ALLOW;
}
}
return MODEACTION_DENY;
}
};
class ModuleNoInvite : public Module
{
NoInvite *ni;
public:
ModuleNoInvite(InspIRCd* Me) : Module(Me)
{
ni = new NoInvite(ServerInstance);
if (!ServerInstance->AddMode(ni, 'V'))
throw ModuleException("Could not add new modes!");
}
void Implements(char* List)
{
List[I_OnUserPreInvite] = 1;
}
virtual int OnUserPreInvite(userrec* user,userrec* dest,chanrec* channel)
{
if (channel->IsModeSet('V'))
{
user->WriteServ("492 %s %s :Can't invite %s to channel (+V set)",user->nick, channel->name, dest->nick);
return 1;
}
return 0;
}
virtual ~ModuleNoInvite()
{
ServerInstance->Modes->DelMode(ni);
DELETE(ni);
}
virtual Version GetVersion()
{
return Version(1,1,0,0,VF_COMMON|VF_VENDOR,API_VERSION);
}
};
MODULE_INIT(ModuleNoInvite)
\ No newline at end of file +/* +------------------------------------+ + * | 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 "users.h" +#include "channels.h" +#include "modules.h" + +/* $ModDesc: Provides support for unreal-style channel mode +V */ + +class NoInvite : public ModeHandler +{ + public: + NoInvite(InspIRCd* Instance) : ModeHandler(Instance, 'V', 0, 0, false, MODETYPE_CHANNEL, false) { } + + ModeAction OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string ¶meter, bool adding) + { + if (adding) + { + if (!channel->IsModeSet('V')) + { + channel->SetMode('V',true); + return MODEACTION_ALLOW; + } + } + else + { + if (channel->IsModeSet('V')) + { + channel->SetMode('V',false); + return MODEACTION_ALLOW; + } + } + + return MODEACTION_DENY; + } +}; + +class ModuleNoInvite : public Module +{ + NoInvite *ni; + public: + + ModuleNoInvite(InspIRCd* Me) : Module(Me) + { + ni = new NoInvite(ServerInstance); + if (!ServerInstance->AddMode(ni, 'V')) + throw ModuleException("Could not add new modes!"); + } + + void Implements(char* List) + { + List[I_OnUserPreInvite] = 1; + } + + virtual int OnUserPreInvite(userrec* user,userrec* dest,chanrec* channel) + { + if (channel->IsModeSet('V')) + { + user->WriteServ("492 %s %s :Can't invite %s to channel (+V set)",user->nick, channel->name, dest->nick); + return 1; + } + return 0; + } + + virtual ~ModuleNoInvite() + { + ServerInstance->Modes->DelMode(ni); + DELETE(ni); + } + + virtual Version GetVersion() + { + return Version(1,1,0,0,VF_COMMON|VF_VENDOR,API_VERSION); + } +}; + +MODULE_INIT(ModuleNoInvite) diff --git a/src/modules/m_nokicks.cpp b/src/modules/m_nokicks.cpp index ac78b4d7a..315eb7399 100644 --- a/src/modules/m_nokicks.cpp +++ b/src/modules/m_nokicks.cpp @@ -1 +1,105 @@ -/* +------------------------------------+
* | 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 "users.h"
#include "channels.h"
#include "modules.h"
/* $ModDesc: Provides support for unreal-style channel mode +Q */
class NoKicks : public ModeHandler
{
public:
NoKicks(InspIRCd* Instance) : ModeHandler(Instance, 'Q', 0, 0, false, MODETYPE_CHANNEL, false) { }
ModeAction OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string ¶meter, bool adding)
{
if (adding)
{
if (!channel->IsModeSet('Q'))
{
channel->SetMode('Q',true);
return MODEACTION_ALLOW;
}
}
else
{
if (channel->IsModeSet('Q'))
{
channel->SetMode('Q',false);
return MODEACTION_ALLOW;
}
}
return MODEACTION_DENY;
}
};
class ModuleNoKicks : public Module
{
NoKicks* nk;
public:
ModuleNoKicks(InspIRCd* Me)
: Module(Me)
{
nk = new NoKicks(ServerInstance);
if (!ServerInstance->AddMode(nk, 'Q'))
throw ModuleException("Could not add new modes!");
}
void Implements(char* List)
{
List[I_OnAccessCheck] = 1;
}
virtual int OnAccessCheck(userrec* source,userrec* dest,chanrec* channel,int access_type)
{
if (access_type == AC_KICK)
{
if (channel->IsModeSet('Q'))
{
if ((ServerInstance->ULine(source->nick)) || (ServerInstance->ULine(source->server)) || (!*source->server))
{
// ulines can still kick with +Q in place
return ACR_ALLOW;
}
else
{
// nobody else can (not even opers with override, and founders)
source->WriteServ("484 %s %s :Can't kick user %s from channel (+Q set)",source->nick, channel->name,dest->nick);
return ACR_DENY;
}
}
}
return ACR_DEFAULT;
}
virtual ~ModuleNoKicks()
{
ServerInstance->Modes->DelMode(nk);
DELETE(nk);
}
virtual Version GetVersion()
{
return Version(1,1,0,0,VF_COMMON|VF_VENDOR,API_VERSION);
}
};
MODULE_INIT(ModuleNoKicks)
\ No newline at end of file +/* +------------------------------------+ + * | 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 "users.h" +#include "channels.h" +#include "modules.h" + +/* $ModDesc: Provides support for unreal-style channel mode +Q */ + +class NoKicks : public ModeHandler +{ + public: + NoKicks(InspIRCd* Instance) : ModeHandler(Instance, 'Q', 0, 0, false, MODETYPE_CHANNEL, false) { } + + ModeAction OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string ¶meter, bool adding) + { + if (adding) + { + if (!channel->IsModeSet('Q')) + { + channel->SetMode('Q',true); + return MODEACTION_ALLOW; + } + } + else + { + if (channel->IsModeSet('Q')) + { + channel->SetMode('Q',false); + return MODEACTION_ALLOW; + } + } + + return MODEACTION_DENY; + } +}; + +class ModuleNoKicks : public Module +{ + + NoKicks* nk; + + public: + + ModuleNoKicks(InspIRCd* Me) + : Module(Me) + { + + nk = new NoKicks(ServerInstance); + if (!ServerInstance->AddMode(nk, 'Q')) + throw ModuleException("Could not add new modes!"); + } + + void Implements(char* List) + { + List[I_OnAccessCheck] = 1; + } + + virtual int OnAccessCheck(userrec* source,userrec* dest,chanrec* channel,int access_type) + { + if (access_type == AC_KICK) + { + if (channel->IsModeSet('Q')) + { + if ((ServerInstance->ULine(source->nick)) || (ServerInstance->ULine(source->server)) || (!*source->server)) + { + // ulines can still kick with +Q in place + return ACR_ALLOW; + } + else + { + // nobody else can (not even opers with override, and founders) + source->WriteServ("484 %s %s :Can't kick user %s from channel (+Q set)",source->nick, channel->name,dest->nick); + return ACR_DENY; + } + } + } + return ACR_DEFAULT; + } + + virtual ~ModuleNoKicks() + { + ServerInstance->Modes->DelMode(nk); + DELETE(nk); + } + + virtual Version GetVersion() + { + return Version(1,1,0,0,VF_COMMON|VF_VENDOR,API_VERSION); + } +}; + + +MODULE_INIT(ModuleNoKicks) diff --git a/src/modules/m_nonicks.cpp b/src/modules/m_nonicks.cpp index d6e6553e9..bb1843a95 100644 --- a/src/modules/m_nonicks.cpp +++ b/src/modules/m_nonicks.cpp @@ -1 +1,102 @@ -/* +------------------------------------+
* | 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 "users.h"
#include "channels.h"
#include "modules.h"
#include "hashcomp.h"
#include "configreader.h"
/* $ModDesc: Provides support for channel mode +N which prevents nick changes on channel */
class NoNicks : public ModeHandler
{
public:
NoNicks(InspIRCd* Instance) : ModeHandler(Instance, 'N', 0, 0, false, MODETYPE_CHANNEL, false) { }
ModeAction OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string ¶meter, bool adding)
{
if (adding)
{
if (!channel->IsModeSet('N'))
{
channel->SetMode('N',true);
return MODEACTION_ALLOW;
}
}
else
{
if (channel->IsModeSet('N'))
{
channel->SetMode('N',false);
return MODEACTION_ALLOW;
}
}
return MODEACTION_DENY;
}
};
class ModuleNoNickChange : public Module
{
NoNicks* nn;
public:
ModuleNoNickChange(InspIRCd* Me)
: Module(Me)
{
nn = new NoNicks(ServerInstance);
ServerInstance->AddMode(nn, 'N');
}
virtual ~ModuleNoNickChange()
{
ServerInstance->Modes->DelMode(nn);
DELETE(nn);
}
virtual Version GetVersion()
{
return Version(1,1,0,1,VF_COMMON|VF_VENDOR,API_VERSION);
}
void Implements(char* List)
{
List[I_OnUserPreNick] = 1;
}
virtual int OnUserPreNick(userrec* user, const std::string &newnick)
{
if (IS_LOCAL(user))
{
for (UCListIter i = user->chans.begin(); i != user->chans.end(); i++)
{
chanrec* curr = i->first;
if (curr->IsModeSet('N'))
{
if (CHANOPS_EXEMPT(ServerInstance, 'N') && curr->GetStatus(user) == STATUS_OP)
continue;
user->WriteServ("447 %s :Can't change nickname while on %s (+N is set)", user->nick, curr->name);
return 1;
}
}
}
return 0;
}
};
MODULE_INIT(ModuleNoNickChange)
\ No newline at end of file +/* +------------------------------------+ + * | 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 "users.h" +#include "channels.h" +#include "modules.h" +#include "hashcomp.h" +#include "configreader.h" + +/* $ModDesc: Provides support for channel mode +N which prevents nick changes on channel */ + +class NoNicks : public ModeHandler +{ + public: + NoNicks(InspIRCd* Instance) : ModeHandler(Instance, 'N', 0, 0, false, MODETYPE_CHANNEL, false) { } + + ModeAction OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string ¶meter, bool adding) + { + if (adding) + { + if (!channel->IsModeSet('N')) + { + channel->SetMode('N',true); + return MODEACTION_ALLOW; + } + } + else + { + if (channel->IsModeSet('N')) + { + channel->SetMode('N',false); + return MODEACTION_ALLOW; + } + } + + return MODEACTION_DENY; + } +}; + +class ModuleNoNickChange : public Module +{ + NoNicks* nn; + public: + ModuleNoNickChange(InspIRCd* Me) + : Module(Me) + { + + nn = new NoNicks(ServerInstance); + ServerInstance->AddMode(nn, 'N'); + } + + virtual ~ModuleNoNickChange() + { + ServerInstance->Modes->DelMode(nn); + DELETE(nn); + } + + virtual Version GetVersion() + { + return Version(1,1,0,1,VF_COMMON|VF_VENDOR,API_VERSION); + } + + void Implements(char* List) + { + List[I_OnUserPreNick] = 1; + } + + virtual int OnUserPreNick(userrec* user, const std::string &newnick) + { + if (IS_LOCAL(user)) + { + for (UCListIter i = user->chans.begin(); i != user->chans.end(); i++) + { + chanrec* curr = i->first; + + if (curr->IsModeSet('N')) + { + if (CHANOPS_EXEMPT(ServerInstance, 'N') && curr->GetStatus(user) == STATUS_OP) + continue; + + user->WriteServ("447 %s :Can't change nickname while on %s (+N is set)", user->nick, curr->name); + return 1; + } + } + } + + return 0; + } +}; + +MODULE_INIT(ModuleNoNickChange) diff --git a/src/modules/m_nonotice.cpp b/src/modules/m_nonotice.cpp index fd4c474cb..ae926b4bb 100644 --- a/src/modules/m_nonotice.cpp +++ b/src/modules/m_nonotice.cpp @@ -1 +1,103 @@ -/* +------------------------------------+
* | 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 "users.h"
#include "channels.h"
#include "modules.h"
/* $ModDesc: Provides support for unreal-style channel mode +T */
class NoNotice : public ModeHandler
{
public:
NoNotice(InspIRCd* Instance) : ModeHandler(Instance, 'T', 0, 0, false, MODETYPE_CHANNEL, false) { }
ModeAction OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string ¶meter, bool adding)
{
if (adding)
{
if (!channel->IsModeSet('T'))
{
channel->SetMode('T',true);
return MODEACTION_ALLOW;
}
}
else
{
if (channel->IsModeSet('T'))
{
channel->SetMode('T',false);
return MODEACTION_ALLOW;
}
}
return MODEACTION_DENY;
}
};
class ModuleNoNotice : public Module
{
NoNotice* nt;
public:
ModuleNoNotice(InspIRCd* Me)
: Module(Me)
{
nt = new NoNotice(ServerInstance);
if (!ServerInstance->AddMode(nt, 'T'))
throw ModuleException("Could not add new modes!");
}
void Implements(char* List)
{
List[I_OnUserPreNotice] = 1;
}
virtual int OnUserPreNotice(userrec* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list)
{
if ((target_type == TYPE_CHANNEL) && (IS_LOCAL(user)))
{
chanrec* c = (chanrec*)dest;
if (c->IsModeSet('T'))
{
if ((ServerInstance->ULine(user->server)) || (c->GetStatus(user) == STATUS_OP) || (c->GetStatus(user) == STATUS_HOP))
{
// ops and halfops can still /NOTICE the channel
return 0;
}
else
{
user->WriteServ("404 %s %s :Can't send NOTICE to channel (+T set)",user->nick, c->name);
return 1;
}
}
}
return 0;
}
virtual ~ModuleNoNotice()
{
ServerInstance->Modes->DelMode(nt);
DELETE(nt);
}
virtual Version GetVersion()
{
return Version(1,1,0,0,VF_COMMON|VF_VENDOR,API_VERSION);
}
};
MODULE_INIT(ModuleNoNotice)
\ No newline at end of file +/* +------------------------------------+ + * | 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 "users.h" +#include "channels.h" +#include "modules.h" + +/* $ModDesc: Provides support for unreal-style channel mode +T */ + +class NoNotice : public ModeHandler +{ + public: + NoNotice(InspIRCd* Instance) : ModeHandler(Instance, 'T', 0, 0, false, MODETYPE_CHANNEL, false) { } + + ModeAction OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string ¶meter, bool adding) + { + if (adding) + { + if (!channel->IsModeSet('T')) + { + channel->SetMode('T',true); + return MODEACTION_ALLOW; + } + } + else + { + if (channel->IsModeSet('T')) + { + channel->SetMode('T',false); + return MODEACTION_ALLOW; + } + } + + return MODEACTION_DENY; + } +}; + +class ModuleNoNotice : public Module +{ + + NoNotice* nt; + public: + + ModuleNoNotice(InspIRCd* Me) + : Module(Me) + { + + nt = new NoNotice(ServerInstance); + if (!ServerInstance->AddMode(nt, 'T')) + throw ModuleException("Could not add new modes!"); + } + + void Implements(char* List) + { + List[I_OnUserPreNotice] = 1; + } + + virtual int OnUserPreNotice(userrec* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list) + { + if ((target_type == TYPE_CHANNEL) && (IS_LOCAL(user))) + { + chanrec* c = (chanrec*)dest; + if (c->IsModeSet('T')) + { + if ((ServerInstance->ULine(user->server)) || (c->GetStatus(user) == STATUS_OP) || (c->GetStatus(user) == STATUS_HOP)) + { + // ops and halfops can still /NOTICE the channel + return 0; + } + else + { + user->WriteServ("404 %s %s :Can't send NOTICE to channel (+T set)",user->nick, c->name); + return 1; + } + } + } + return 0; + } + + virtual ~ModuleNoNotice() + { + ServerInstance->Modes->DelMode(nt); + DELETE(nt); + } + + virtual Version GetVersion() + { + return Version(1,1,0,0,VF_COMMON|VF_VENDOR,API_VERSION); + } +}; + +MODULE_INIT(ModuleNoNotice) diff --git a/src/modules/m_oper_hash.cpp b/src/modules/m_oper_hash.cpp index a3989ad91..b4661741b 100644 --- a/src/modules/m_oper_hash.cpp +++ b/src/modules/m_oper_hash.cpp @@ -1 +1,163 @@ -/* +------------------------------------+
* | 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.
*
* ---------------------------------------------------
*/
/* $ModDesc: Allows for hashed oper passwords */
/* $ModDep: m_hash.h */
#include "inspircd.h"
#include "users.h"
#include "channels.h"
#include "modules.h"
#include "m_hash.h"
typedef std::map<irc::string, Module*> hashymodules;
/* Handle /MKPASSWD
*/
class cmd_mkpasswd : public command_t
{
Module* Sender;
hashymodules &hashers;
std::deque<std::string> &names;
public:
cmd_mkpasswd (InspIRCd* Instance, Module* S, hashymodules &h, std::deque<std::string> &n)
: command_t(Instance,"MKPASSWD", 'o', 2), Sender(S), hashers(h), names(n)
{
this->source = "m_oper_hash.so";
syntax = "<hashtype> <any-text>";
}
void MakeHash(userrec* user, const char* algo, const char* stuff)
{
/* Lets see if they gave us an algorithm which has been implemented */
hashymodules::iterator x = hashers.find(algo);
if (x != hashers.end())
{
/* Yup, reset it first (Always ALWAYS do this) */
HashResetRequest(Sender, x->second).Send();
/* Now attempt to generate a hash */
user->WriteServ("NOTICE %s :%s hashed password for %s is %s",user->nick, algo, stuff, HashSumRequest(Sender, x->second, stuff).Send() );
}
else
{
/* I dont do flying, bob. */
user->WriteServ("NOTICE %s :Unknown hash type, valid hash types are: %s", user->nick, irc::stringjoiner(", ", names, 0, names.size() - 1).GetJoined().c_str() );
}
}
CmdResult Handle (const char** parameters, int pcnt, userrec *user)
{
MakeHash(user, parameters[0], parameters[1]);
/* NOTE: Don't propogate this across the network!
* We dont want plaintext passes going all over the place...
* To make sure it goes nowhere, return CMD_FAILURE!
*/
return CMD_FAILURE;
}
};
class ModuleOperHash : public Module
{
cmd_mkpasswd* mycommand;
ConfigReader* Conf;
hashymodules hashers; /* List of modules which implement HashRequest */
std::deque<std::string> names; /* Module names which implement HashRequest */
public:
ModuleOperHash(InspIRCd* Me)
: Module(Me)
{
/* Read the config file first */
Conf = NULL;
OnRehash(NULL,"");
ServerInstance->UseInterface("HashRequest");
/* Find all modules which implement the interface 'HashRequest' */
modulelist* ml = ServerInstance->FindInterface("HashRequest");
/* Did we find any modules? */
if (ml)
{
/* Yes, enumerate them all to find out the hashing algorithm name */
for (modulelist::iterator m = ml->begin(); m != ml->end(); m++)
{
/* Make a request to it for its name, its implementing
* HashRequest so we know its safe to do this
*/
std::string name = HashNameRequest(this, *m).Send();
/* Build a map of them */
hashers[name.c_str()] = *m;
names.push_back(name);
}
}
else
{
throw ModuleException("I can't find any modules loaded which implement the HashRequest interface! You probably forgot to load a hashing module such as m_md5.so or m_sha256.so.");
}
mycommand = new cmd_mkpasswd(ServerInstance, this, hashers, names);
ServerInstance->AddCommand(mycommand);
}
virtual ~ModuleOperHash()
{
ServerInstance->DoneWithInterface("HashRequest");
}
void Implements(char* List)
{
List[I_OnRehash] = List[I_OnOperCompare] = 1;
}
virtual void OnRehash(userrec* user, const std::string ¶meter)
{
/* Re-read configuration file */
if (Conf)
delete Conf;
Conf = new ConfigReader(ServerInstance);
}
virtual int OnOperCompare(const std::string &data, const std::string &input, int tagnumber)
{
/* First, lets see what hash theyre using on this oper */
std::string hashtype = Conf->ReadValue("oper", "hash", tagnumber);
hashymodules::iterator x = hashers.find(hashtype.c_str());
/* Is this a valid hash name? (case insensitive) */
if (x != hashers.end())
{
/* Reset the hashing module */
HashResetRequest(this, x->second).Send();
/* Compare the hash in the config to the generated hash */
if (!strcasecmp(data.c_str(), HashSumRequest(this, x->second, input.c_str()).Send()))
return 1;
/* No match, and must be hashed, forbid */
else return -1;
}
/* Not a hash, fall through to strcmp in core */
return 0;
}
virtual Version GetVersion()
{
return Version(1,1,0,1,VF_VENDOR,API_VERSION);
}
};
MODULE_INIT(ModuleOperHash)
\ No newline at end of file +/* +------------------------------------+ + * | 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. + * + * --------------------------------------------------- + */ + +/* $ModDesc: Allows for hashed oper passwords */ +/* $ModDep: m_hash.h */ + +#include "inspircd.h" +#include "users.h" +#include "channels.h" +#include "modules.h" +#include "m_hash.h" + +typedef std::map<irc::string, Module*> hashymodules; + +/* Handle /MKPASSWD + */ +class cmd_mkpasswd : public command_t +{ + Module* Sender; + hashymodules &hashers; + std::deque<std::string> &names; + public: + cmd_mkpasswd (InspIRCd* Instance, Module* S, hashymodules &h, std::deque<std::string> &n) + : command_t(Instance,"MKPASSWD", 'o', 2), Sender(S), hashers(h), names(n) + { + this->source = "m_oper_hash.so"; + syntax = "<hashtype> <any-text>"; + } + + void MakeHash(userrec* user, const char* algo, const char* stuff) + { + /* Lets see if they gave us an algorithm which has been implemented */ + hashymodules::iterator x = hashers.find(algo); + if (x != hashers.end()) + { + /* Yup, reset it first (Always ALWAYS do this) */ + HashResetRequest(Sender, x->second).Send(); + /* Now attempt to generate a hash */ + user->WriteServ("NOTICE %s :%s hashed password for %s is %s",user->nick, algo, stuff, HashSumRequest(Sender, x->second, stuff).Send() ); + } + else + { + /* I dont do flying, bob. */ + user->WriteServ("NOTICE %s :Unknown hash type, valid hash types are: %s", user->nick, irc::stringjoiner(", ", names, 0, names.size() - 1).GetJoined().c_str() ); + } + } + + CmdResult Handle (const char** parameters, int pcnt, userrec *user) + { + MakeHash(user, parameters[0], parameters[1]); + /* NOTE: Don't propogate this across the network! + * We dont want plaintext passes going all over the place... + * To make sure it goes nowhere, return CMD_FAILURE! + */ + return CMD_FAILURE; + } +}; + +class ModuleOperHash : public Module +{ + + cmd_mkpasswd* mycommand; + ConfigReader* Conf; + hashymodules hashers; /* List of modules which implement HashRequest */ + std::deque<std::string> names; /* Module names which implement HashRequest */ + + public: + + ModuleOperHash(InspIRCd* Me) + : Module(Me) + { + + /* Read the config file first */ + Conf = NULL; + OnRehash(NULL,""); + + ServerInstance->UseInterface("HashRequest"); + + /* Find all modules which implement the interface 'HashRequest' */ + modulelist* ml = ServerInstance->FindInterface("HashRequest"); + + /* Did we find any modules? */ + if (ml) + { + /* Yes, enumerate them all to find out the hashing algorithm name */ + for (modulelist::iterator m = ml->begin(); m != ml->end(); m++) + { + /* Make a request to it for its name, its implementing + * HashRequest so we know its safe to do this + */ + std::string name = HashNameRequest(this, *m).Send(); + /* Build a map of them */ + hashers[name.c_str()] = *m; + names.push_back(name); + } + } + else + { + throw ModuleException("I can't find any modules loaded which implement the HashRequest interface! You probably forgot to load a hashing module such as m_md5.so or m_sha256.so."); + } + + mycommand = new cmd_mkpasswd(ServerInstance, this, hashers, names); + ServerInstance->AddCommand(mycommand); + } + + virtual ~ModuleOperHash() + { + ServerInstance->DoneWithInterface("HashRequest"); + } + + void Implements(char* List) + { + List[I_OnRehash] = List[I_OnOperCompare] = 1; + } + + virtual void OnRehash(userrec* user, const std::string ¶meter) + { + /* Re-read configuration file */ + if (Conf) + delete Conf; + + Conf = new ConfigReader(ServerInstance); + } + + virtual int OnOperCompare(const std::string &data, const std::string &input, int tagnumber) + { + /* First, lets see what hash theyre using on this oper */ + std::string hashtype = Conf->ReadValue("oper", "hash", tagnumber); + hashymodules::iterator x = hashers.find(hashtype.c_str()); + + /* Is this a valid hash name? (case insensitive) */ + if (x != hashers.end()) + { + /* Reset the hashing module */ + HashResetRequest(this, x->second).Send(); + /* Compare the hash in the config to the generated hash */ + if (!strcasecmp(data.c_str(), HashSumRequest(this, x->second, input.c_str()).Send())) + return 1; + /* No match, and must be hashed, forbid */ + else return -1; + } + + /* Not a hash, fall through to strcmp in core */ + return 0; + } + + virtual Version GetVersion() + { + return Version(1,1,0,1,VF_VENDOR,API_VERSION); + } +}; + +MODULE_INIT(ModuleOperHash) diff --git a/src/modules/m_operchans.cpp b/src/modules/m_operchans.cpp index 493904e35..6fd6c9e98 100644 --- a/src/modules/m_operchans.cpp +++ b/src/modules/m_operchans.cpp @@ -1 +1,97 @@ -/* +------------------------------------+
* | 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 "users.h"
#include "channels.h"
#include "modules.h"
/* $ModDesc: Provides support for oper-only chans via the +O channel mode */
class OperChans : public ModeHandler
{
public:
/* This is an oper-only mode */
OperChans(InspIRCd* Instance) : ModeHandler(Instance, 'O', 0, 0, false, MODETYPE_CHANNEL, true) { }
ModeAction OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string ¶meter, bool adding)
{
if (adding)
{
if (!channel->IsModeSet('O'))
{
channel->SetMode('O',true);
return MODEACTION_ALLOW;
}
}
else
{
if (channel->IsModeSet('O'))
{
channel->SetMode('O',false);
return MODEACTION_ALLOW;
}
}
return MODEACTION_DENY;
}
};
class ModuleOperChans : public Module
{
OperChans* oc;
public:
ModuleOperChans(InspIRCd* Me)
: Module(Me)
{
oc = new OperChans(ServerInstance);
if (!ServerInstance->AddMode(oc, 'O'))
throw ModuleException("Could not add new modes!");
}
void Implements(char* List)
{
List[I_OnUserPreJoin] = 1;
}
virtual int OnUserPreJoin(userrec* user, chanrec* chan, const char* cname, std::string &privs)
{
if (!IS_OPER(user))
{
if (chan)
{
if (chan->IsModeSet('O'))
{
user->WriteServ("520 %s %s :Only IRC operators may join the channel %s (+O is set)",user->nick, chan->name,chan->name);
return 1;
}
}
}
return 0;
}
virtual ~ModuleOperChans()
{
ServerInstance->Modes->DelMode(oc);
DELETE(oc);
}
virtual Version GetVersion()
{
return Version(1,1,0,0,VF_VENDOR|VF_COMMON,API_VERSION);
}
};
MODULE_INIT(ModuleOperChans)
\ No newline at end of file +/* +------------------------------------+ + * | 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 "users.h" +#include "channels.h" +#include "modules.h" + +/* $ModDesc: Provides support for oper-only chans via the +O channel mode */ + +class OperChans : public ModeHandler +{ + public: + /* This is an oper-only mode */ + OperChans(InspIRCd* Instance) : ModeHandler(Instance, 'O', 0, 0, false, MODETYPE_CHANNEL, true) { } + + ModeAction OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string ¶meter, bool adding) + { + if (adding) + { + if (!channel->IsModeSet('O')) + { + channel->SetMode('O',true); + return MODEACTION_ALLOW; + } + } + else + { + if (channel->IsModeSet('O')) + { + channel->SetMode('O',false); + return MODEACTION_ALLOW; + } + } + + return MODEACTION_DENY; + } +}; + +class ModuleOperChans : public Module +{ + + OperChans* oc; + public: + ModuleOperChans(InspIRCd* Me) + : Module(Me) + { + + oc = new OperChans(ServerInstance); + if (!ServerInstance->AddMode(oc, 'O')) + throw ModuleException("Could not add new modes!"); + } + + void Implements(char* List) + { + List[I_OnUserPreJoin] = 1; + } + + virtual int OnUserPreJoin(userrec* user, chanrec* chan, const char* cname, std::string &privs) + { + if (!IS_OPER(user)) + { + if (chan) + { + if (chan->IsModeSet('O')) + { + user->WriteServ("520 %s %s :Only IRC operators may join the channel %s (+O is set)",user->nick, chan->name,chan->name); + return 1; + } + } + } + return 0; + } + + virtual ~ModuleOperChans() + { + ServerInstance->Modes->DelMode(oc); + DELETE(oc); + } + + virtual Version GetVersion() + { + return Version(1,1,0,0,VF_VENDOR|VF_COMMON,API_VERSION); + } +}; + +MODULE_INIT(ModuleOperChans) diff --git a/src/modules/m_operjoin.cpp b/src/modules/m_operjoin.cpp index d69112eba..d12bc1932 100644 --- a/src/modules/m_operjoin.cpp +++ b/src/modules/m_operjoin.cpp @@ -1 +1,90 @@ -/* +------------------------------------+
* | 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 "users.h"
#include "channels.h"
#include "modules.h"
/* $ModDesc: Forces opers to join the specified channel(s) on oper-up */
class ModuleOperjoin : public Module
{
private:
std::string operChan;
std::vector<std::string> operChans;
int tokenize(const string &str, std::vector<std::string> &tokens)
{
// skip delimiters at beginning.
string::size_type lastPos = str.find_first_not_of(",", 0);
// find first "non-delimiter".
string::size_type pos = str.find_first_of(",", lastPos);
while (string::npos != pos || string::npos != lastPos)
{
// found a token, add it to the vector.
tokens.push_back(str.substr(lastPos, pos - lastPos));
// skip delimiters. Note the "not_of"
lastPos = str.find_first_not_of(",", pos);
// find next "non-delimiter"
pos = str.find_first_of(",", lastPos);
}
return tokens.size();
}
public:
ModuleOperjoin(InspIRCd* Me) : Module(Me)
{
OnRehash(NULL, "");
}
void Implements(char* List)
{
List[I_OnPostOper] = List[I_OnRehash] = 1;
}
virtual void OnRehash(userrec* user, const std::string ¶meter)
{
ConfigReader* conf = new ConfigReader(ServerInstance);
operChan = conf->ReadValue("operjoin", "channel", 0);
operChans.clear();
if (!operChan.empty())
tokenize(operChan,operChans);
DELETE(conf);
}
virtual ~ModuleOperjoin()
{
}
virtual Version GetVersion()
{
return Version(1,1,0,1,VF_VENDOR,API_VERSION);
}
virtual void OnPostOper(userrec* user, const std::string &opertype)
{
if (!IS_LOCAL(user))
return;
for(std::vector<std::string>::iterator it = operChans.begin(); it != operChans.end(); it++)
if (ServerInstance->IsChannel(it->c_str()))
chanrec::JoinUser(ServerInstance, user, it->c_str(), false, "", ServerInstance->Time(true));
}
};
MODULE_INIT(ModuleOperjoin)
\ No newline at end of file +/* +------------------------------------+ + * | 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 "users.h" +#include "channels.h" +#include "modules.h" + +/* $ModDesc: Forces opers to join the specified channel(s) on oper-up */ + +class ModuleOperjoin : public Module +{ + private: + std::string operChan; + std::vector<std::string> operChans; + + int tokenize(const string &str, std::vector<std::string> &tokens) + { + // skip delimiters at beginning. + string::size_type lastPos = str.find_first_not_of(",", 0); + // find first "non-delimiter". + string::size_type pos = str.find_first_of(",", lastPos); + + while (string::npos != pos || string::npos != lastPos) + { + // found a token, add it to the vector. + tokens.push_back(str.substr(lastPos, pos - lastPos)); + // skip delimiters. Note the "not_of" + lastPos = str.find_first_not_of(",", pos); + // find next "non-delimiter" + pos = str.find_first_of(",", lastPos); + } + return tokens.size(); + } + + public: + ModuleOperjoin(InspIRCd* Me) : Module(Me) + { + OnRehash(NULL, ""); + } + + void Implements(char* List) + { + List[I_OnPostOper] = List[I_OnRehash] = 1; + } + + virtual void OnRehash(userrec* user, const std::string ¶meter) + { + ConfigReader* conf = new ConfigReader(ServerInstance); + + operChan = conf->ReadValue("operjoin", "channel", 0); + operChans.clear(); + if (!operChan.empty()) + tokenize(operChan,operChans); + + DELETE(conf); + } + + virtual ~ModuleOperjoin() + { + } + + virtual Version GetVersion() + { + return Version(1,1,0,1,VF_VENDOR,API_VERSION); + } + + virtual void OnPostOper(userrec* user, const std::string &opertype) + { + if (!IS_LOCAL(user)) + return; + + for(std::vector<std::string>::iterator it = operChans.begin(); it != operChans.end(); it++) + if (ServerInstance->IsChannel(it->c_str())) + chanrec::JoinUser(ServerInstance, user, it->c_str(), false, "", ServerInstance->Time(true)); + } + +}; + +MODULE_INIT(ModuleOperjoin) diff --git a/src/modules/m_operlevels.cpp b/src/modules/m_operlevels.cpp index 6d3aa7b14..918d444ac 100644 --- a/src/modules/m_operlevels.cpp +++ b/src/modules/m_operlevels.cpp @@ -1 +1,122 @@ -/* +------------------------------------+
* | 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 "users.h"
#include "channels.h"
#include "modules.h"
/* $ModDesc: Gives each oper type a 'level', cannot kill opers 'above' your level. */
class ModuleOperLevels : public Module
{
private:
ConfigReader* conf;
public:
ModuleOperLevels(InspIRCd* Me)
: Module(Me)
{
conf = new ConfigReader(ServerInstance);
}
virtual ~ModuleOperLevels()
{
DELETE(conf);
}
void Implements(char* List)
{
List[I_OnRehash] = List[I_OnKill] = 1;
}
virtual void OnRehash(userrec* user, const std::string ¶meter)
{
DELETE(conf);
conf = new ConfigReader(ServerInstance);
}
virtual Version GetVersion()
{
return Version(1,1,0,1,VF_VENDOR,API_VERSION);
}
virtual int OnKill(userrec* source, userrec* dest, const std::string &reason)
{
long dest_level = 0,source_level = 0;
// oper killing an oper?
if (IS_OPER(dest) && IS_OPER(source))
{
for (int j =0; j < conf->Enumerate("type"); j++)
{
std::string typen = conf->ReadValue("type","name",j);
if (!strcmp(typen.c_str(),dest->oper))
{
dest_level = conf->ReadInteger("type","level",j,true);
break;
}
}
for (int k =0; k < conf->Enumerate("type"); k++)
{
std::string typen = conf->ReadValue("type","name",k);
if (!strcmp(typen.c_str(),source->oper))
{
source_level = conf->ReadInteger("type","level",k,true);
break;
}
}
if (dest_level > source_level)
{
ServerInstance->WriteOpers("Oper %s (level %d) attempted to /kill a higher oper: %s (level %d): Reason: %s",source->nick,source_level,dest->nick,dest_level,reason.c_str());
dest->WriteServ("NOTICE %s :Oper %s attempted to /kill you!",dest->nick,source->nick);
source->WriteServ("481 %s :Permission Denied - Oper %s is a higher level than you",source->nick,dest->nick);
return 1;
}
}
return 0;
}
};
class ModuleOperLevelsFactory : public ModuleFactory
{
public:
ModuleOperLevelsFactory()
{
}
~ModuleOperLevelsFactory()
{
}
virtual Module * CreateModule(InspIRCd* Me)
{
return new ModuleOperLevels(Me);
}
};
extern "C" DllExport void * init_module( void )
{
return new ModuleOperLevelsFactory;
}
\ No newline at end of file +/* +------------------------------------+ + * | 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 "users.h" +#include "channels.h" +#include "modules.h" + +/* $ModDesc: Gives each oper type a 'level', cannot kill opers 'above' your level. */ + + + +class ModuleOperLevels : public Module +{ + + private: + + + ConfigReader* conf; + + public: + + ModuleOperLevels(InspIRCd* Me) + : Module(Me) + { + + + conf = new ConfigReader(ServerInstance); + } + + virtual ~ModuleOperLevels() + { + DELETE(conf); + } + + void Implements(char* List) + { + List[I_OnRehash] = List[I_OnKill] = 1; + } + + virtual void OnRehash(userrec* user, const std::string ¶meter) + { + DELETE(conf); + conf = new ConfigReader(ServerInstance); + } + + virtual Version GetVersion() + { + return Version(1,1,0,1,VF_VENDOR,API_VERSION); + } + + virtual int OnKill(userrec* source, userrec* dest, const std::string &reason) + { + long dest_level = 0,source_level = 0; + + // oper killing an oper? + if (IS_OPER(dest) && IS_OPER(source)) + { + for (int j =0; j < conf->Enumerate("type"); j++) + { + std::string typen = conf->ReadValue("type","name",j); + if (!strcmp(typen.c_str(),dest->oper)) + { + dest_level = conf->ReadInteger("type","level",j,true); + break; + } + } + for (int k =0; k < conf->Enumerate("type"); k++) + { + std::string typen = conf->ReadValue("type","name",k); + if (!strcmp(typen.c_str(),source->oper)) + { + source_level = conf->ReadInteger("type","level",k,true); + break; + } + } + if (dest_level > source_level) + { + ServerInstance->WriteOpers("Oper %s (level %d) attempted to /kill a higher oper: %s (level %d): Reason: %s",source->nick,source_level,dest->nick,dest_level,reason.c_str()); + dest->WriteServ("NOTICE %s :Oper %s attempted to /kill you!",dest->nick,source->nick); + source->WriteServ("481 %s :Permission Denied - Oper %s is a higher level than you",source->nick,dest->nick); + return 1; + } + } + return 0; + } + +}; + +class ModuleOperLevelsFactory : public ModuleFactory +{ + public: + ModuleOperLevelsFactory() + { + } + + ~ModuleOperLevelsFactory() + { + } + + virtual Module * CreateModule(InspIRCd* Me) + { + return new ModuleOperLevels(Me); + } + +}; + +extern "C" DllExport void * init_module( void ) +{ + return new ModuleOperLevelsFactory; +} + diff --git a/src/modules/m_operlog.cpp b/src/modules/m_operlog.cpp index 1a7d5bd8a..9bbdbef25 100644 --- a/src/modules/m_operlog.cpp +++ b/src/modules/m_operlog.cpp @@ -1 +1,75 @@ -/* +------------------------------------+
* | 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 "users.h"
#include "channels.h"
#include "modules.h"
/* $ModDesc: A module which logs all oper commands to the ircd log at default loglevel. */
class ModuleOperLog : public Module
{
private:
public:
ModuleOperLog(InspIRCd* Me) : Module(Me)
{
}
virtual ~ModuleOperLog()
{
}
virtual Version GetVersion()
{
return Version(1,1,0,0,VF_VENDOR,API_VERSION);
}
void Implements(char* List)
{
List[I_OnPreCommand] = List[I_On005Numeric] = 1;
}
virtual int OnPreCommand(const std::string &command, const char** parameters, int pcnt, userrec *user, bool validated, const std::string &original_line)
{
/* If the command doesnt appear to be valid, we dont want to mess with it. */
if (!validated)
return 0;
if ((IS_OPER(user)) && (IS_LOCAL(user)) && (user->HasPermission(command)))
{
command_t* thiscommand = ServerInstance->Parser->GetHandler(command);
if ((thiscommand) && (thiscommand->flags_needed = 'o'))
{
std::string plist;
for (int j = 0; j < pcnt; j++)
plist.append(std::string(" ")+std::string(parameters[j]));
ServerInstance->Log(DEFAULT,"OPERLOG: [%s!%s@%s] %s%s",user->nick,user->ident,user->host,command.c_str(),plist.c_str());
}
}
return 0;
}
virtual void On005Numeric(std::string &output)
{
output.append(" OPERLOG");
}
};
MODULE_INIT(ModuleOperLog)
\ No newline at end of file +/* +------------------------------------+ + * | 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 "users.h" +#include "channels.h" +#include "modules.h" + +/* $ModDesc: A module which logs all oper commands to the ircd log at default loglevel. */ + +class ModuleOperLog : public Module +{ + private: + + public: + ModuleOperLog(InspIRCd* Me) : Module(Me) + { + + } + + virtual ~ModuleOperLog() + { + } + + virtual Version GetVersion() + { + return Version(1,1,0,0,VF_VENDOR,API_VERSION); + } + + void Implements(char* List) + { + List[I_OnPreCommand] = List[I_On005Numeric] = 1; + } + + virtual int OnPreCommand(const std::string &command, const char** parameters, int pcnt, userrec *user, bool validated, const std::string &original_line) + { + /* If the command doesnt appear to be valid, we dont want to mess with it. */ + if (!validated) + return 0; + + if ((IS_OPER(user)) && (IS_LOCAL(user)) && (user->HasPermission(command))) + { + command_t* thiscommand = ServerInstance->Parser->GetHandler(command); + if ((thiscommand) && (thiscommand->flags_needed = 'o')) + { + std::string plist; + for (int j = 0; j < pcnt; j++) + plist.append(std::string(" ")+std::string(parameters[j])); + + ServerInstance->Log(DEFAULT,"OPERLOG: [%s!%s@%s] %s%s",user->nick,user->ident,user->host,command.c_str(),plist.c_str()); + } + } + + return 0; + } + + virtual void On005Numeric(std::string &output) + { + output.append(" OPERLOG"); + } + +}; + + +MODULE_INIT(ModuleOperLog) diff --git a/src/modules/m_opermodes.cpp b/src/modules/m_opermodes.cpp index 05d7a2b42..598f84e43 100644 --- a/src/modules/m_opermodes.cpp +++ b/src/modules/m_opermodes.cpp @@ -1 +1,109 @@ -/* +------------------------------------+
* | 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 "users.h"
#include "channels.h"
#include "modules.h"
/* $ModDesc: Sets (and unsets) modes on opers when they oper up */
class ModuleModesOnOper : public Module
{
private:
ConfigReader *Conf;
public:
ModuleModesOnOper(InspIRCd* Me)
: Module(Me)
{
Conf = new ConfigReader(ServerInstance);
}
void Implements(char* List)
{
List[I_OnPostOper] = List[I_OnRehash] = 1;
}
virtual void OnRehash(userrec* user, const std::string ¶meter)
{
DELETE(Conf);
Conf = new ConfigReader(ServerInstance);
}
virtual ~ModuleModesOnOper()
{
DELETE(Conf);
}
virtual Version GetVersion()
{
return Version(1,1,0,1,VF_VENDOR,API_VERSION);
}
virtual void OnPostOper(userrec* user, const std::string &opertype)
{
// whenever a user opers, go through the oper types, find their <type:modes>,
// and if they have one apply their modes. The mode string can contain +modes
// to add modes to the user or -modes to take modes from the user.
for (int j =0; j < Conf->Enumerate("type"); j++)
{
std::string typen = Conf->ReadValue("type","name",j);
if (!strcmp(typen.c_str(),user->oper))
{
std::string ThisOpersModes = Conf->ReadValue("type","modes",j);
if (!ThisOpersModes.empty())
{
char first = *(ThisOpersModes.c_str());
if ((first != '+') && (first != '-'))
ThisOpersModes = "+" + ThisOpersModes;
std::string buf;
stringstream ss(ThisOpersModes);
vector<string> tokens;
// split ThisOperModes into modes and mode params
while (ss >> buf)
tokens.push_back(buf);
int size = tokens.size() + 1;
const char** modes = new const char*[size];
modes[0] = user->nick;
// process mode params
int i = 1;
for (unsigned int k = 0; k < tokens.size(); k++)
{
modes[i] = tokens[k].c_str();
i++;
}
std::deque<std::string> n;
Event rmode((char *)&n, NULL, "send_mode_explicit");
for (unsigned int j = 0; j < tokens.size(); j++)
n.push_back(modes[j]);
rmode.Send(ServerInstance);
ServerInstance->SendMode(modes, size, user);
delete [] modes;
}
break;
}
}
}
};
MODULE_INIT(ModuleModesOnOper)
\ No newline at end of file +/* +------------------------------------+ + * | 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 "users.h" +#include "channels.h" +#include "modules.h" + +/* $ModDesc: Sets (and unsets) modes on opers when they oper up */ + +class ModuleModesOnOper : public Module +{ + private: + + + ConfigReader *Conf; + + public: + ModuleModesOnOper(InspIRCd* Me) + : Module(Me) + { + + Conf = new ConfigReader(ServerInstance); + } + + void Implements(char* List) + { + List[I_OnPostOper] = List[I_OnRehash] = 1; + } + + virtual void OnRehash(userrec* user, const std::string ¶meter) + { + DELETE(Conf); + Conf = new ConfigReader(ServerInstance); + } + + virtual ~ModuleModesOnOper() + { + DELETE(Conf); + } + + virtual Version GetVersion() + { + return Version(1,1,0,1,VF_VENDOR,API_VERSION); + } + + virtual void OnPostOper(userrec* user, const std::string &opertype) + { + // whenever a user opers, go through the oper types, find their <type:modes>, + // and if they have one apply their modes. The mode string can contain +modes + // to add modes to the user or -modes to take modes from the user. + for (int j =0; j < Conf->Enumerate("type"); j++) + { + std::string typen = Conf->ReadValue("type","name",j); + if (!strcmp(typen.c_str(),user->oper)) + { + std::string ThisOpersModes = Conf->ReadValue("type","modes",j); + if (!ThisOpersModes.empty()) + { + char first = *(ThisOpersModes.c_str()); + if ((first != '+') && (first != '-')) + ThisOpersModes = "+" + ThisOpersModes; + + std::string buf; + stringstream ss(ThisOpersModes); + vector<string> tokens; + + // split ThisOperModes into modes and mode params + while (ss >> buf) + tokens.push_back(buf); + + int size = tokens.size() + 1; + const char** modes = new const char*[size]; + modes[0] = user->nick; + + // process mode params + int i = 1; + for (unsigned int k = 0; k < tokens.size(); k++) + { + modes[i] = tokens[k].c_str(); + i++; + } + + std::deque<std::string> n; + Event rmode((char *)&n, NULL, "send_mode_explicit"); + for (unsigned int j = 0; j < tokens.size(); j++) + n.push_back(modes[j]); + + rmode.Send(ServerInstance); + ServerInstance->SendMode(modes, size, user); + delete [] modes; + } + break; + } + } + } +}; + +MODULE_INIT(ModuleModesOnOper) diff --git a/src/modules/m_opermotd.cpp b/src/modules/m_opermotd.cpp index 7e48eefdb..55608dcb8 100644 --- a/src/modules/m_opermotd.cpp +++ b/src/modules/m_opermotd.cpp @@ -1 +1,116 @@ -/* +------------------------------------+
* | 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 "users.h"
#include "channels.h"
#include "modules.h"
/* $ModDesc: Shows a message to opers after oper-up, adds /opermotd */
static FileReader* opermotd;
CmdResult ShowOperMOTD(userrec* user)
{
if(!opermotd->FileSize())
{
user->WriteServ(std::string("425 ") + user->nick + std::string(" :OPERMOTD file is missing"));
return CMD_FAILURE;
}
user->WriteServ(std::string("375 ") + user->nick + std::string(" :- IRC Operators Message of the Day"));
for(int i=0; i != opermotd->FileSize(); i++)
{
user->WriteServ(std::string("372 ") + user->nick + std::string(" :- ") + opermotd->GetLine(i));
}
user->WriteServ(std::string("376 ") + user->nick + std::string(" :- End of OPERMOTD"));
/* don't route me */
return CMD_LOCALONLY;
}
/** Handle /OPERMOTD
*/
class cmd_opermotd : public command_t
{
public:
cmd_opermotd (InspIRCd* Instance) : command_t(Instance,"OPERMOTD", 'o', 0)
{
this->source = "m_opermotd.so";
syntax = "[<servername>]";
}
CmdResult Handle (const char** parameters, int pcnt, userrec* user)
{
return ShowOperMOTD(user);
}
};
class ModuleOpermotd : public Module
{
cmd_opermotd* mycommand;
public:
void LoadOperMOTD()
{
ConfigReader* conf = new ConfigReader(ServerInstance);
std::string filename;
filename = conf->ReadValue("opermotd","file",0);
if (opermotd)
{
delete opermotd;
opermotd = NULL;
}
opermotd = new FileReader(ServerInstance, filename);
DELETE(conf);
}
ModuleOpermotd(InspIRCd* Me)
: Module(Me)
{
opermotd = NULL;
mycommand = new cmd_opermotd(ServerInstance);
ServerInstance->AddCommand(mycommand);
opermotd = new FileReader(ServerInstance);
LoadOperMOTD();
}
virtual ~ModuleOpermotd()
{
}
virtual Version GetVersion()
{
return Version(1,1,0,1,VF_VENDOR,API_VERSION);
}
void Implements(char* List)
{
List[I_OnRehash] = List[I_OnOper] = 1;
}
virtual void OnOper(userrec* user, const std::string &opertype)
{
ShowOperMOTD(user);
}
virtual void OnRehash(userrec* user, const std::string ¶meter)
{
LoadOperMOTD();
}
};
MODULE_INIT(ModuleOpermotd)
\ No newline at end of file +/* +------------------------------------+ + * | 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 "users.h" +#include "channels.h" +#include "modules.h" + +/* $ModDesc: Shows a message to opers after oper-up, adds /opermotd */ + +static FileReader* opermotd; + +CmdResult ShowOperMOTD(userrec* user) +{ + if(!opermotd->FileSize()) + { + user->WriteServ(std::string("425 ") + user->nick + std::string(" :OPERMOTD file is missing")); + return CMD_FAILURE; + } + + user->WriteServ(std::string("375 ") + user->nick + std::string(" :- IRC Operators Message of the Day")); + + for(int i=0; i != opermotd->FileSize(); i++) + { + user->WriteServ(std::string("372 ") + user->nick + std::string(" :- ") + opermotd->GetLine(i)); + } + + user->WriteServ(std::string("376 ") + user->nick + std::string(" :- End of OPERMOTD")); + + /* don't route me */ + return CMD_LOCALONLY; +} + +/** Handle /OPERMOTD + */ +class cmd_opermotd : public command_t +{ + public: + cmd_opermotd (InspIRCd* Instance) : command_t(Instance,"OPERMOTD", 'o', 0) + { + this->source = "m_opermotd.so"; + syntax = "[<servername>]"; + } + + CmdResult Handle (const char** parameters, int pcnt, userrec* user) + { + return ShowOperMOTD(user); + } +}; + + +class ModuleOpermotd : public Module +{ + cmd_opermotd* mycommand; + public: + + void LoadOperMOTD() + { + ConfigReader* conf = new ConfigReader(ServerInstance); + std::string filename; + filename = conf->ReadValue("opermotd","file",0); + if (opermotd) + { + delete opermotd; + opermotd = NULL; + } + opermotd = new FileReader(ServerInstance, filename); + DELETE(conf); + } + + ModuleOpermotd(InspIRCd* Me) + : Module(Me) + { + opermotd = NULL; + mycommand = new cmd_opermotd(ServerInstance); + ServerInstance->AddCommand(mycommand); + opermotd = new FileReader(ServerInstance); + LoadOperMOTD(); + } + + virtual ~ModuleOpermotd() + { + } + + virtual Version GetVersion() + { + return Version(1,1,0,1,VF_VENDOR,API_VERSION); + } + + void Implements(char* List) + { + List[I_OnRehash] = List[I_OnOper] = 1; + } + + virtual void OnOper(userrec* user, const std::string &opertype) + { + ShowOperMOTD(user); + } + + virtual void OnRehash(userrec* user, const std::string ¶meter) + { + LoadOperMOTD(); + } +}; + +MODULE_INIT(ModuleOpermotd) diff --git a/src/modules/m_override.cpp b/src/modules/m_override.cpp index c5b343552..be123d4fd 100644 --- a/src/modules/m_override.cpp +++ b/src/modules/m_override.cpp @@ -1 +1,294 @@ -/* +------------------------------------+
* | 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 "users.h"
#include "channels.h"
#include "modules.h"
#include "configreader.h"
#include "wildcard.h"
/* $ModDesc: Provides support for unreal-style oper-override */
typedef std::map<std::string,std::string> override_t;
class ModuleOverride : public Module
{
override_t overrides;
bool NoisyOverride;
bool OverriddenMode;
int OverOps, OverDeops, OverVoices, OverDevoices, OverHalfops, OverDehalfops;
public:
ModuleOverride(InspIRCd* Me)
: Module(Me)
{
// read our config options (main config file)
OnRehash(NULL,"");
ServerInstance->SNO->EnableSnomask('O',"OVERRIDE");
OverriddenMode = false;
OverOps = OverDeops = OverVoices = OverDevoices = OverHalfops = OverDehalfops = 0;
}
virtual void OnRehash(userrec* user, const std::string ¶meter)
{
// on a rehash we delete our classes for good measure and create them again.
ConfigReader* Conf = new ConfigReader(ServerInstance);
// re-read our config options on a rehash
NoisyOverride = Conf->ReadFlag("override","noisy",0);
overrides.clear();
for (int j =0; j < Conf->Enumerate("type"); j++)
{
std::string typen = Conf->ReadValue("type","name",j);
std::string tokenlist = Conf->ReadValue("type","override",j);
overrides[typen] = tokenlist;
}
DELETE(Conf);
}
void Implements(char* List)
{
List[I_OnRehash] = List[I_OnAccessCheck] = List[I_On005Numeric] = List[I_OnUserPreJoin] = List[I_OnUserPreKick] = List[I_OnPostCommand] = 1;
}
virtual void OnPostCommand(const std::string &command, const char** parameters, int pcnt, userrec *user, CmdResult result, const std::string &original_line)
{
if ((NoisyOverride) && (OverriddenMode) && (irc::string(command.c_str()) == "MODE") && (result == CMD_SUCCESS))
{
int Total = OverOps + OverDeops + OverVoices + OverDevoices + OverHalfops + OverDehalfops;
ServerInstance->SNO->WriteToSnoMask('O',std::string(user->nick)+" Overriding modes: "+ServerInstance->Modes->GetLastParse()+" "+(Total ? "[Detail: " : "")+
(OverOps ? ConvToStr(OverOps)+" op"+(OverOps != 1 ? "s" : "")+" " : "")+
(OverDeops ? ConvToStr(OverDeops)+" deop"+(OverDeops != 1 ? "s" : "")+" " : "")+
(OverVoices ? ConvToStr(OverVoices)+" voice"+(OverVoices != 1 ? "s" : "")+" " : "")+
(OverDevoices ? ConvToStr(OverDevoices)+" devoice"+(OverDevoices != 1 ? "s" : "")+" " : "")+
(OverHalfops ? ConvToStr(OverHalfops)+" halfop"+(OverHalfops != 1 ? "s" : "")+" " : "")+
(OverDehalfops ? ConvToStr(OverDehalfops)+" dehalfop"+(OverDehalfops != 1 ? "s" : "") : "")
+(Total ? "]" : ""));
OverriddenMode = false;
OverOps = OverDeops = OverVoices = OverDevoices = OverHalfops = OverDehalfops = 0;
}
}
virtual void On005Numeric(std::string &output)
{
output.append(" OVERRIDE");
}
virtual bool CanOverride(userrec* source, char* token)
{
// checks to see if the oper's type has <type:override>
override_t::iterator j = overrides.find(source->oper);
if (j != overrides.end())
{
// its defined or * is set, return its value as a boolean for if the token is set
return ((j->second.find(token, 0) != std::string::npos) || (j->second.find("*", 0) != std::string::npos));
}
// its not defined at all, count as false
return false;
}
virtual int OnUserPreKick(userrec* source, userrec* user, chanrec* chan, const std::string &reason)
{
if (IS_OPER(source) && CanOverride(source,"KICK"))
{
if (((chan->GetStatus(source) == STATUS_HOP) && (chan->GetStatus(user) == STATUS_OP)) || (chan->GetStatus(source) < STATUS_VOICE))
{
ServerInstance->SNO->WriteToSnoMask('O',std::string(source->nick)+" Override-Kicked "+std::string(user->nick)+" on "+std::string(chan->name)+" ("+reason+")");
}
/* Returning -1 explicitly allows the kick */
return -1;
}
return 0;
}
virtual int OnAccessCheck(userrec* source,userrec* dest,chanrec* channel,int access_type)
{
if (IS_OPER(source))
{
if (source && channel)
{
// Fix by brain - allow the change if they arent on channel - rely on boolean short-circuit
// to not check the other items in the statement if they arent on the channel
int mode = channel->GetStatus(source);
switch (access_type)
{
case AC_DEOP:
if (CanOverride(source,"MODEDEOP"))
{
if (NoisyOverride)
if ((!channel->HasUser(source)) || (mode < STATUS_OP))
OverDeops++;
return ACR_ALLOW;
}
else
{
return ACR_DEFAULT;
}
break;
case AC_OP:
if (CanOverride(source,"MODEOP"))
{
if (NoisyOverride)
if ((!channel->HasUser(source)) || (mode < STATUS_OP))
OverOps++;
return ACR_ALLOW;
}
else
{
return ACR_DEFAULT;
}
break;
case AC_VOICE:
if (CanOverride(source,"MODEVOICE"))
{
if (NoisyOverride)
if ((!channel->HasUser(source)) || (mode < STATUS_HOP))
OverVoices++;
return ACR_ALLOW;
}
else
{
return ACR_DEFAULT;
}
break;
case AC_DEVOICE:
if (CanOverride(source,"MODEDEVOICE"))
{
if (NoisyOverride)
if ((!channel->HasUser(source)) || (mode < STATUS_HOP))
OverDevoices++;
return ACR_ALLOW;
}
else
{
return ACR_DEFAULT;
}
break;
case AC_HALFOP:
if (CanOverride(source,"MODEHALFOP"))
{
if (NoisyOverride)
if ((!channel->HasUser(source)) || (mode < STATUS_OP))
OverHalfops++;
return ACR_ALLOW;
}
else
{
return ACR_DEFAULT;
}
break;
case AC_DEHALFOP:
if (CanOverride(source,"MODEDEHALFOP"))
{
if (NoisyOverride)
if ((!channel->HasUser(source)) || (mode < STATUS_OP))
OverDehalfops++;
return ACR_ALLOW;
}
else
{
return ACR_DEFAULT;
}
break;
}
if (CanOverride(source,"OTHERMODE"))
{
if (NoisyOverride)
if ((!channel->HasUser(source)) || (mode < STATUS_OP))
{
OverriddenMode = true;
OverOps = OverDeops = OverVoices = OverDevoices = OverHalfops = OverDehalfops = 0;
}
return ACR_ALLOW;
}
else
{
return ACR_DEFAULT;
}
}
}
return ACR_DEFAULT;
}
virtual int OnUserPreJoin(userrec* user, chanrec* chan, const char* cname, std::string &privs)
{
if (IS_OPER(user))
{
if (chan)
{
if ((chan->modes[CM_INVITEONLY]) && (CanOverride(user,"INVITE")))
{
irc::string x = chan->name;
if (!user->IsInvited(x))
{
/* XXX - Ugly cast for a parameter that isn't used? :< - Om */
if (NoisyOverride)
chan->WriteChannelWithServ(ServerInstance->Config->ServerName, "NOTICE %s :%s used oper-override to bypass invite-only", cname, user->nick);
ServerInstance->SNO->WriteToSnoMask('O',std::string(user->nick)+" used operoverride to bypass +i on "+std::string(cname));
}
return -1;
}
if ((*chan->key) && (CanOverride(user,"KEY")))
{
if (NoisyOverride)
chan->WriteChannelWithServ(ServerInstance->Config->ServerName, "NOTICE %s :%s used oper-override to bypass the channel key", cname, user->nick);
ServerInstance->SNO->WriteToSnoMask('O',std::string(user->nick)+" used operoverride to bypass +k on "+std::string(cname));
return -1;
}
if ((chan->limit > 0) && (chan->GetUserCounter() >= chan->limit) && (CanOverride(user,"LIMIT")))
{
if (NoisyOverride)
chan->WriteChannelWithServ(ServerInstance->Config->ServerName, "NOTICE %s :%s used oper-override to bypass the channel limit", cname, user->nick);
ServerInstance->SNO->WriteToSnoMask('O',std::string(user->nick)+" used operoverride to bypass +l on "+std::string(cname));
return -1;
}
if (CanOverride(user,"BANWALK"))
{
if (chan->IsBanned(user))
{
if (NoisyOverride)
chan->WriteChannelWithServ(ServerInstance->Config->ServerName, "NOTICE %s :%s used oper-override to bypass channel ban", cname, user->nick);
ServerInstance->SNO->WriteToSnoMask('O',"%s used oper-override to bypass channel ban on %s", user->nick, cname);
}
return -1;
}
}
}
return 0;
}
virtual ~ModuleOverride()
{
ServerInstance->SNO->DisableSnomask('O');
}
virtual Version GetVersion()
{
return Version(1,1,0,1,VF_VENDOR,API_VERSION);
}
};
MODULE_INIT(ModuleOverride)
\ No newline at end of file +/* +------------------------------------+ + * | 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 "users.h" +#include "channels.h" +#include "modules.h" +#include "configreader.h" +#include "wildcard.h" + +/* $ModDesc: Provides support for unreal-style oper-override */ + +typedef std::map<std::string,std::string> override_t; + +class ModuleOverride : public Module +{ + + override_t overrides; + bool NoisyOverride; + bool OverriddenMode; + int OverOps, OverDeops, OverVoices, OverDevoices, OverHalfops, OverDehalfops; + + public: + + ModuleOverride(InspIRCd* Me) + : Module(Me) + { + // read our config options (main config file) + OnRehash(NULL,""); + ServerInstance->SNO->EnableSnomask('O',"OVERRIDE"); + OverriddenMode = false; + OverOps = OverDeops = OverVoices = OverDevoices = OverHalfops = OverDehalfops = 0; + } + + virtual void OnRehash(userrec* user, const std::string ¶meter) + { + // on a rehash we delete our classes for good measure and create them again. + ConfigReader* Conf = new ConfigReader(ServerInstance); + + // re-read our config options on a rehash + NoisyOverride = Conf->ReadFlag("override","noisy",0); + overrides.clear(); + for (int j =0; j < Conf->Enumerate("type"); j++) + { + std::string typen = Conf->ReadValue("type","name",j); + std::string tokenlist = Conf->ReadValue("type","override",j); + overrides[typen] = tokenlist; + } + + DELETE(Conf); + } + + void Implements(char* List) + { + List[I_OnRehash] = List[I_OnAccessCheck] = List[I_On005Numeric] = List[I_OnUserPreJoin] = List[I_OnUserPreKick] = List[I_OnPostCommand] = 1; + } + + virtual void OnPostCommand(const std::string &command, const char** parameters, int pcnt, userrec *user, CmdResult result, const std::string &original_line) + { + if ((NoisyOverride) && (OverriddenMode) && (irc::string(command.c_str()) == "MODE") && (result == CMD_SUCCESS)) + { + int Total = OverOps + OverDeops + OverVoices + OverDevoices + OverHalfops + OverDehalfops; + + ServerInstance->SNO->WriteToSnoMask('O',std::string(user->nick)+" Overriding modes: "+ServerInstance->Modes->GetLastParse()+" "+(Total ? "[Detail: " : "")+ + (OverOps ? ConvToStr(OverOps)+" op"+(OverOps != 1 ? "s" : "")+" " : "")+ + (OverDeops ? ConvToStr(OverDeops)+" deop"+(OverDeops != 1 ? "s" : "")+" " : "")+ + (OverVoices ? ConvToStr(OverVoices)+" voice"+(OverVoices != 1 ? "s" : "")+" " : "")+ + (OverDevoices ? ConvToStr(OverDevoices)+" devoice"+(OverDevoices != 1 ? "s" : "")+" " : "")+ + (OverHalfops ? ConvToStr(OverHalfops)+" halfop"+(OverHalfops != 1 ? "s" : "")+" " : "")+ + (OverDehalfops ? ConvToStr(OverDehalfops)+" dehalfop"+(OverDehalfops != 1 ? "s" : "") : "") + +(Total ? "]" : "")); + + OverriddenMode = false; + OverOps = OverDeops = OverVoices = OverDevoices = OverHalfops = OverDehalfops = 0; + } + } + + virtual void On005Numeric(std::string &output) + { + output.append(" OVERRIDE"); + } + + virtual bool CanOverride(userrec* source, char* token) + { + // checks to see if the oper's type has <type:override> + override_t::iterator j = overrides.find(source->oper); + + if (j != overrides.end()) + { + // its defined or * is set, return its value as a boolean for if the token is set + return ((j->second.find(token, 0) != std::string::npos) || (j->second.find("*", 0) != std::string::npos)); + } + + // its not defined at all, count as false + return false; + } + + virtual int OnUserPreKick(userrec* source, userrec* user, chanrec* chan, const std::string &reason) + { + if (IS_OPER(source) && CanOverride(source,"KICK")) + { + if (((chan->GetStatus(source) == STATUS_HOP) && (chan->GetStatus(user) == STATUS_OP)) || (chan->GetStatus(source) < STATUS_VOICE)) + { + ServerInstance->SNO->WriteToSnoMask('O',std::string(source->nick)+" Override-Kicked "+std::string(user->nick)+" on "+std::string(chan->name)+" ("+reason+")"); + } + /* Returning -1 explicitly allows the kick */ + return -1; + } + return 0; + } + + virtual int OnAccessCheck(userrec* source,userrec* dest,chanrec* channel,int access_type) + { + if (IS_OPER(source)) + { + if (source && channel) + { + // Fix by brain - allow the change if they arent on channel - rely on boolean short-circuit + // to not check the other items in the statement if they arent on the channel + int mode = channel->GetStatus(source); + switch (access_type) + { + case AC_DEOP: + if (CanOverride(source,"MODEDEOP")) + { + if (NoisyOverride) + if ((!channel->HasUser(source)) || (mode < STATUS_OP)) + OverDeops++; + return ACR_ALLOW; + } + else + { + return ACR_DEFAULT; + } + break; + case AC_OP: + if (CanOverride(source,"MODEOP")) + { + if (NoisyOverride) + if ((!channel->HasUser(source)) || (mode < STATUS_OP)) + OverOps++; + return ACR_ALLOW; + } + else + { + return ACR_DEFAULT; + } + break; + case AC_VOICE: + if (CanOverride(source,"MODEVOICE")) + { + if (NoisyOverride) + if ((!channel->HasUser(source)) || (mode < STATUS_HOP)) + OverVoices++; + return ACR_ALLOW; + } + else + { + return ACR_DEFAULT; + } + break; + case AC_DEVOICE: + if (CanOverride(source,"MODEDEVOICE")) + { + if (NoisyOverride) + if ((!channel->HasUser(source)) || (mode < STATUS_HOP)) + OverDevoices++; + return ACR_ALLOW; + } + else + { + return ACR_DEFAULT; + } + break; + case AC_HALFOP: + if (CanOverride(source,"MODEHALFOP")) + { + if (NoisyOverride) + if ((!channel->HasUser(source)) || (mode < STATUS_OP)) + OverHalfops++; + return ACR_ALLOW; + } + else + { + return ACR_DEFAULT; + } + break; + case AC_DEHALFOP: + if (CanOverride(source,"MODEDEHALFOP")) + { + if (NoisyOverride) + if ((!channel->HasUser(source)) || (mode < STATUS_OP)) + OverDehalfops++; + return ACR_ALLOW; + } + else + { + return ACR_DEFAULT; + } + break; + } + + if (CanOverride(source,"OTHERMODE")) + { + if (NoisyOverride) + if ((!channel->HasUser(source)) || (mode < STATUS_OP)) + { + OverriddenMode = true; + OverOps = OverDeops = OverVoices = OverDevoices = OverHalfops = OverDehalfops = 0; + } + return ACR_ALLOW; + } + else + { + return ACR_DEFAULT; + } + } + } + + return ACR_DEFAULT; + } + + virtual int OnUserPreJoin(userrec* user, chanrec* chan, const char* cname, std::string &privs) + { + if (IS_OPER(user)) + { + if (chan) + { + if ((chan->modes[CM_INVITEONLY]) && (CanOverride(user,"INVITE"))) + { + irc::string x = chan->name; + if (!user->IsInvited(x)) + { + /* XXX - Ugly cast for a parameter that isn't used? :< - Om */ + if (NoisyOverride) + chan->WriteChannelWithServ(ServerInstance->Config->ServerName, "NOTICE %s :%s used oper-override to bypass invite-only", cname, user->nick); + ServerInstance->SNO->WriteToSnoMask('O',std::string(user->nick)+" used operoverride to bypass +i on "+std::string(cname)); + } + return -1; + } + + if ((*chan->key) && (CanOverride(user,"KEY"))) + { + if (NoisyOverride) + chan->WriteChannelWithServ(ServerInstance->Config->ServerName, "NOTICE %s :%s used oper-override to bypass the channel key", cname, user->nick); + ServerInstance->SNO->WriteToSnoMask('O',std::string(user->nick)+" used operoverride to bypass +k on "+std::string(cname)); + return -1; + } + + if ((chan->limit > 0) && (chan->GetUserCounter() >= chan->limit) && (CanOverride(user,"LIMIT"))) + { + if (NoisyOverride) + chan->WriteChannelWithServ(ServerInstance->Config->ServerName, "NOTICE %s :%s used oper-override to bypass the channel limit", cname, user->nick); + ServerInstance->SNO->WriteToSnoMask('O',std::string(user->nick)+" used operoverride to bypass +l on "+std::string(cname)); + return -1; + } + + if (CanOverride(user,"BANWALK")) + { + if (chan->IsBanned(user)) + { + if (NoisyOverride) + chan->WriteChannelWithServ(ServerInstance->Config->ServerName, "NOTICE %s :%s used oper-override to bypass channel ban", cname, user->nick); + ServerInstance->SNO->WriteToSnoMask('O',"%s used oper-override to bypass channel ban on %s", user->nick, cname); + } + return -1; + } + } + } + return 0; + } + + virtual ~ModuleOverride() + { + ServerInstance->SNO->DisableSnomask('O'); + } + + virtual Version GetVersion() + { + return Version(1,1,0,1,VF_VENDOR,API_VERSION); + } +}; + +MODULE_INIT(ModuleOverride) diff --git a/src/modules/m_randquote.cpp b/src/modules/m_randquote.cpp index 8ab9aab4e..2ad7831ce 100644 --- a/src/modules/m_randquote.cpp +++ b/src/modules/m_randquote.cpp @@ -1 +1,138 @@ -/* +------------------------------------+
* | 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 "users.h"
#include "channels.h"
#include "modules.h"
static FileReader *quotes = NULL;
std::string q_file;
std::string prefix;
std::string suffix;
/* $ModDesc: Provides random Quotes on Connect. */
/** Handle /RANDQUOTE
*/
class cmd_randquote : public command_t
{
public:
cmd_randquote (InspIRCd* Instance) : command_t(Instance,"RANDQUOTE", 0, 0)
{
this->source = "m_randquote.so";
}
CmdResult Handle (const char** parameters, int pcntl, userrec *user)
{
std::string str;
int fsize;
if (q_file.empty() || quotes->Exists())
{
fsize = quotes->FileSize();
str = quotes->GetLine(rand() % fsize);
user->WriteServ("NOTICE %s :%s%s%s",user->nick,prefix.c_str(),str.c_str(),suffix.c_str());
}
else
{
user->WriteServ("NOTICE %s :Your administrator specified an invalid quotes file, please bug them about this.", user->nick);
return CMD_FAILURE;
}
return CMD_LOCALONLY;
}
};
/** Thrown by m_randquote
*/
class RandquoteException : public ModuleException
{
private:
const std::string err;
public:
RandquoteException(const std::string &message) : err(message) { }
~RandquoteException() throw () { }
virtual const char* GetReason()
{
return err.c_str();
}
};
class ModuleRandQuote : public Module
{
private:
cmd_randquote* mycommand;
ConfigReader *conf;
public:
ModuleRandQuote(InspIRCd* Me)
: Module(Me)
{
conf = new ConfigReader(ServerInstance);
// Sort the Randomizer thingie..
srand(time(NULL));
q_file = conf->ReadValue("randquote","file",0);
prefix = conf->ReadValue("randquote","prefix",0);
suffix = conf->ReadValue("randquote","suffix",0);
mycommand = NULL;
if (q_file.empty())
{
RandquoteException e("m_randquote: Quotefile not specified - Please check your config.");
throw(e);
}
quotes = new FileReader(ServerInstance, q_file);
if(!quotes->Exists())
{
RandquoteException e("m_randquote: QuoteFile not Found!! Please check your config - module will not function.");
throw(e);
}
else
{
/* Hidden Command -- Mode clients assume /quote sends raw data to an IRCd >:D */
mycommand = new cmd_randquote(ServerInstance);
ServerInstance->AddCommand(mycommand);
}
}
void Implements(char* List)
{
List[I_OnUserConnect] = 1;
}
virtual ~ModuleRandQuote()
{
DELETE(conf);
DELETE(quotes);
}
virtual Version GetVersion()
{
return Version(1,1,0,1,VF_VENDOR,API_VERSION);
}
virtual void OnUserConnect(userrec* user)
{
if (mycommand)
mycommand->Handle(NULL, 0, user);
}
};
MODULE_INIT(ModuleRandQuote)
\ No newline at end of file +/* +------------------------------------+ + * | 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 "users.h" +#include "channels.h" +#include "modules.h" + +static FileReader *quotes = NULL; + +std::string q_file; +std::string prefix; +std::string suffix; + +/* $ModDesc: Provides random Quotes on Connect. */ + +/** Handle /RANDQUOTE + */ +class cmd_randquote : public command_t +{ + public: + cmd_randquote (InspIRCd* Instance) : command_t(Instance,"RANDQUOTE", 0, 0) + { + this->source = "m_randquote.so"; + } + + CmdResult Handle (const char** parameters, int pcntl, userrec *user) + { + std::string str; + int fsize; + + if (q_file.empty() || quotes->Exists()) + { + fsize = quotes->FileSize(); + str = quotes->GetLine(rand() % fsize); + user->WriteServ("NOTICE %s :%s%s%s",user->nick,prefix.c_str(),str.c_str(),suffix.c_str()); + } + else + { + user->WriteServ("NOTICE %s :Your administrator specified an invalid quotes file, please bug them about this.", user->nick); + return CMD_FAILURE; + } + + return CMD_LOCALONLY; + } +}; + +/** Thrown by m_randquote + */ +class RandquoteException : public ModuleException +{ + private: + const std::string err; + public: + RandquoteException(const std::string &message) : err(message) { } + + ~RandquoteException() throw () { } + + virtual const char* GetReason() + { + return err.c_str(); + } +}; + +class ModuleRandQuote : public Module +{ + private: + cmd_randquote* mycommand; + ConfigReader *conf; + public: + ModuleRandQuote(InspIRCd* Me) + : Module(Me) + { + + conf = new ConfigReader(ServerInstance); + // Sort the Randomizer thingie.. + srand(time(NULL)); + + q_file = conf->ReadValue("randquote","file",0); + prefix = conf->ReadValue("randquote","prefix",0); + suffix = conf->ReadValue("randquote","suffix",0); + + mycommand = NULL; + + if (q_file.empty()) + { + RandquoteException e("m_randquote: Quotefile not specified - Please check your config."); + throw(e); + } + + quotes = new FileReader(ServerInstance, q_file); + if(!quotes->Exists()) + { + RandquoteException e("m_randquote: QuoteFile not Found!! Please check your config - module will not function."); + throw(e); + } + else + { + /* Hidden Command -- Mode clients assume /quote sends raw data to an IRCd >:D */ + mycommand = new cmd_randquote(ServerInstance); + ServerInstance->AddCommand(mycommand); + } + } + + void Implements(char* List) + { + List[I_OnUserConnect] = 1; + } + + virtual ~ModuleRandQuote() + { + DELETE(conf); + DELETE(quotes); + } + + virtual Version GetVersion() + { + return Version(1,1,0,1,VF_VENDOR,API_VERSION); + } + + virtual void OnUserConnect(userrec* user) + { + if (mycommand) + mycommand->Handle(NULL, 0, user); + } +}; + +MODULE_INIT(ModuleRandQuote) diff --git a/src/modules/m_redirect.cpp b/src/modules/m_redirect.cpp index ee2d3f004..a746644c2 100644 --- a/src/modules/m_redirect.cpp +++ b/src/modules/m_redirect.cpp @@ -1 +1,160 @@ -/* +------------------------------------+
* | 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 "users.h"
#include "channels.h"
#include "modules.h"
/* $ModDesc: Provides channel mode +L (limit redirection) */
/** Handle channel mode +L
*/
class Redirect : public ModeHandler
{
public:
Redirect(InspIRCd* Instance) : ModeHandler(Instance, 'L', 1, 0, false, MODETYPE_CHANNEL, false) { }
ModePair ModeSet(userrec* source, userrec* dest, chanrec* channel, const std::string ¶meter)
{
if (channel->IsModeSet('L'))
return std::make_pair(true, channel->GetModeParameter('L'));
else
return std::make_pair(false, parameter);
}
bool CheckTimeStamp(time_t theirs, time_t ours, const std::string &their_param, const std::string &our_param, chanrec* channel)
{
/* When TS is equal, the alphabetically later one wins */
return (their_param < our_param);
}
ModeAction OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string ¶meter, bool adding)
{
if (adding)
{
chanrec* c = NULL;
if (!ServerInstance->IsChannel(parameter.c_str()))
{
source->WriteServ("403 %s %s :Invalid channel name",source->nick, parameter.c_str());
parameter.clear();
return MODEACTION_DENY;
}
c = ServerInstance->FindChan(parameter);
if (c)
{
/* Fix by brain: Dont let a channel be linked to *itself* either */
if (IS_LOCAL(source))
{
if ((c == channel) || (c->IsModeSet('L')))
{
source->WriteServ("690 %s :Circular or chained +L to %s not allowed (Channel already has +L). Pack of wild dogs has been unleashed.",source->nick,parameter.c_str());
parameter.clear();
return MODEACTION_DENY;
}
else
{
for (chan_hash::const_iterator i = ServerInstance->chanlist->begin(); i != ServerInstance->chanlist->end(); i++)
{
if ((i->second != channel) && (i->second->IsModeSet('L')) && (irc::string(i->second->GetModeParameter('L').c_str()) == irc::string(channel->name)))
{
source->WriteServ("690 %s :Circular or chained +L to %s not allowed (Already forwarded here from %s). Angry monkeys dispatched.",source->nick,parameter.c_str(),i->second->name);
return MODEACTION_DENY;
}
}
}
}
}
channel->SetMode('L', true);
channel->SetModeParam('L', parameter.c_str(), true);
return MODEACTION_ALLOW;
}
else
{
if (channel->IsModeSet('L'))
{
channel->SetMode('L', false);
return MODEACTION_ALLOW;
}
}
return MODEACTION_DENY;
}
};
class ModuleRedirect : public Module
{
Redirect* re;
public:
ModuleRedirect(InspIRCd* Me)
: Module(Me)
{
re = new Redirect(ServerInstance);
if (!ServerInstance->AddMode(re, 'L'))
throw ModuleException("Could not add new modes!");
}
void Implements(char* List)
{
List[I_OnUserPreJoin] = 1;
}
virtual int OnUserPreJoin(userrec* user, chanrec* chan, const char* cname, std::string &privs)
{
if (chan)
{
if (chan->IsModeSet('L') && chan->limit)
{
if (chan->GetUserCounter() >= chan->limit)
{
std::string channel = chan->GetModeParameter('L');
/* sometimes broken ulines can make circular or chained +L, avoid this */
chanrec* destchan = NULL;
destchan = ServerInstance->FindChan(channel);
if (destchan && destchan->IsModeSet('L'))
{
user->WriteServ("470 %s :%s is full, but has a circular redirect (+L), not following redirection to %s", user->nick, cname, channel.c_str());
return 1;
}
user->WriteServ("470 %s :%s has become full, so you are automatically being transferred to the linked channel %s", user->nick, cname, channel.c_str());
chanrec::JoinUser(ServerInstance, user, channel.c_str(), false, "", ServerInstance->Time(true));
return 1;
}
}
}
return 0;
}
virtual ~ModuleRedirect()
{
ServerInstance->Modes->DelMode(re);
DELETE(re);
}
virtual Version GetVersion()
{
return Version(1, 1, 0, 0, VF_COMMON | VF_VENDOR, API_VERSION);
}
};
MODULE_INIT(ModuleRedirect)
\ No newline at end of file +/* +------------------------------------+ + * | 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 "users.h" +#include "channels.h" +#include "modules.h" + +/* $ModDesc: Provides channel mode +L (limit redirection) */ + +/** Handle channel mode +L + */ +class Redirect : public ModeHandler +{ + public: + Redirect(InspIRCd* Instance) : ModeHandler(Instance, 'L', 1, 0, false, MODETYPE_CHANNEL, false) { } + + ModePair ModeSet(userrec* source, userrec* dest, chanrec* channel, const std::string ¶meter) + { + if (channel->IsModeSet('L')) + return std::make_pair(true, channel->GetModeParameter('L')); + else + return std::make_pair(false, parameter); + } + + bool CheckTimeStamp(time_t theirs, time_t ours, const std::string &their_param, const std::string &our_param, chanrec* channel) + { + /* When TS is equal, the alphabetically later one wins */ + return (their_param < our_param); + } + + ModeAction OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string ¶meter, bool adding) + { + if (adding) + { + chanrec* c = NULL; + + if (!ServerInstance->IsChannel(parameter.c_str())) + { + source->WriteServ("403 %s %s :Invalid channel name",source->nick, parameter.c_str()); + parameter.clear(); + return MODEACTION_DENY; + } + + c = ServerInstance->FindChan(parameter); + if (c) + { + /* Fix by brain: Dont let a channel be linked to *itself* either */ + if (IS_LOCAL(source)) + { + if ((c == channel) || (c->IsModeSet('L'))) + { + source->WriteServ("690 %s :Circular or chained +L to %s not allowed (Channel already has +L). Pack of wild dogs has been unleashed.",source->nick,parameter.c_str()); + parameter.clear(); + return MODEACTION_DENY; + } + else + { + for (chan_hash::const_iterator i = ServerInstance->chanlist->begin(); i != ServerInstance->chanlist->end(); i++) + { + if ((i->second != channel) && (i->second->IsModeSet('L')) && (irc::string(i->second->GetModeParameter('L').c_str()) == irc::string(channel->name))) + { + source->WriteServ("690 %s :Circular or chained +L to %s not allowed (Already forwarded here from %s). Angry monkeys dispatched.",source->nick,parameter.c_str(),i->second->name); + return MODEACTION_DENY; + } + } + } + } + } + + channel->SetMode('L', true); + channel->SetModeParam('L', parameter.c_str(), true); + return MODEACTION_ALLOW; + } + else + { + if (channel->IsModeSet('L')) + { + channel->SetMode('L', false); + return MODEACTION_ALLOW; + } + } + + return MODEACTION_DENY; + + } +}; + +class ModuleRedirect : public Module +{ + + Redirect* re; + + public: + + ModuleRedirect(InspIRCd* Me) + : Module(Me) + { + + re = new Redirect(ServerInstance); + if (!ServerInstance->AddMode(re, 'L')) + throw ModuleException("Could not add new modes!"); + } + + void Implements(char* List) + { + List[I_OnUserPreJoin] = 1; + } + + virtual int OnUserPreJoin(userrec* user, chanrec* chan, const char* cname, std::string &privs) + { + if (chan) + { + if (chan->IsModeSet('L') && chan->limit) + { + if (chan->GetUserCounter() >= chan->limit) + { + std::string channel = chan->GetModeParameter('L'); + + /* sometimes broken ulines can make circular or chained +L, avoid this */ + chanrec* destchan = NULL; + destchan = ServerInstance->FindChan(channel); + if (destchan && destchan->IsModeSet('L')) + { + user->WriteServ("470 %s :%s is full, but has a circular redirect (+L), not following redirection to %s", user->nick, cname, channel.c_str()); + return 1; + } + + user->WriteServ("470 %s :%s has become full, so you are automatically being transferred to the linked channel %s", user->nick, cname, channel.c_str()); + chanrec::JoinUser(ServerInstance, user, channel.c_str(), false, "", ServerInstance->Time(true)); + return 1; + } + } + } + return 0; + } + + virtual ~ModuleRedirect() + { + ServerInstance->Modes->DelMode(re); + DELETE(re); + } + + virtual Version GetVersion() + { + return Version(1, 1, 0, 0, VF_COMMON | VF_VENDOR, API_VERSION); + } +}; + +MODULE_INIT(ModuleRedirect) diff --git a/src/modules/m_regonlycreate.cpp b/src/modules/m_regonlycreate.cpp index 0d911a0bf..2174c860c 100644 --- a/src/modules/m_regonlycreate.cpp +++ b/src/modules/m_regonlycreate.cpp @@ -1 +1,61 @@ -/* +------------------------------------+
* | 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 "users.h"
#include "channels.h"
#include "modules.h"
/* $ModDesc: Prevents users who's nicks are not registered from creating new channels */
class ModuleRegOnlyCreate : public Module
{
public:
ModuleRegOnlyCreate(InspIRCd* Me)
: Module(Me)
{
}
void Implements(char* List)
{
List[I_OnUserPreJoin] = 1;
}
virtual int OnUserPreJoin(userrec* user, chanrec* chan, const char* cname, std::string &privs)
{
if (chan)
return 0;
if (IS_OPER(user))
return 0;
if ((!user->IsModeSet('r')) && (!user->GetExt("accountname")))
{
user->WriteServ("482 %s %s :You must have a registered nickname to create a new channel", user->nick, cname);
return 1;
}
return 0;
}
virtual ~ModuleRegOnlyCreate()
{
}
virtual Version GetVersion()
{
return Version(1, 1, 0, 0, VF_VENDOR, API_VERSION);
}
};
MODULE_INIT(ModuleRegOnlyCreate)
\ No newline at end of file +/* +------------------------------------+ + * | 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 "users.h" +#include "channels.h" +#include "modules.h" + +/* $ModDesc: Prevents users who's nicks are not registered from creating new channels */ + +class ModuleRegOnlyCreate : public Module +{ + public: + ModuleRegOnlyCreate(InspIRCd* Me) + : Module(Me) + { + } + + void Implements(char* List) + { + List[I_OnUserPreJoin] = 1; + } + + virtual int OnUserPreJoin(userrec* user, chanrec* chan, const char* cname, std::string &privs) + { + if (chan) + return 0; + + if (IS_OPER(user)) + return 0; + + if ((!user->IsModeSet('r')) && (!user->GetExt("accountname"))) + { + user->WriteServ("482 %s %s :You must have a registered nickname to create a new channel", user->nick, cname); + return 1; + } + + return 0; + } + + virtual ~ModuleRegOnlyCreate() + { + } + + virtual Version GetVersion() + { + return Version(1, 1, 0, 0, VF_VENDOR, API_VERSION); + } +}; + +MODULE_INIT(ModuleRegOnlyCreate) diff --git a/src/modules/m_remove.cpp b/src/modules/m_remove.cpp index 6d9be00ad..d28087ba8 100644 --- a/src/modules/m_remove.cpp +++ b/src/modules/m_remove.cpp @@ -1 +1,288 @@ -/* +------------------------------------+
* | 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 <sstream>
#include "users.h"
#include "channels.h"
#include "modules.h"
#include "configreader.h"
/* $ModDesc: Provides a /remove command, this is mostly an alternative to /kick, except makes users appear to have parted the channel */
/*
* This module supports the use of the +q and +a usermodes, but should work without them too.
* Usage of the command is restricted to +hoaq, and you cannot remove a user with a "higher" level than yourself.
* eg: +h can remove +hv and users with no modes. +a can remove +aohv and users with no modes.
*/
/** Base class for /FPART and /REMOVE
*/
class RemoveBase
{
private:
bool& supportnokicks;
InspIRCd* ServerInstance;
protected:
RemoveBase(InspIRCd* Instance, bool& snk) : supportnokicks(snk), ServerInstance(Instance)
{
}
enum ModeLevel { PEON = 0, HALFOP = 1, OP = 2, ADMIN = 3, OWNER = 4, ULINE = 5 };
/* This little function just converts a chanmode character (U ~ & @ & +) into an integer (5 4 3 2 1 0) */
/* XXX - We should probably use the new mode prefix rank stuff
* for this instead now -- Brain */
ModeLevel chartolevel(const std::string &privs)
{
if(privs.empty())
{
return PEON;
}
switch (privs[0])
{
case 'U':
/* Ulined */
return ULINE;
case '~':
/* Owner */
return OWNER;
case '&':
/* Admin */
return ADMIN;
case '@':
/* Operator */
return OP;
case '%':
/* Halfop */
return HALFOP;
default:
/* Peon */
return PEON;
}
}
CmdResult Handle (const char** parameters, int pcnt, userrec *user, bool neworder)
{
const char* channame;
const char* username;
userrec* target;
chanrec* channel;
ModeLevel tlevel;
ModeLevel ulevel;
std::string reason;
std::string protectkey;
std::string founderkey;
bool hasnokicks;
/* Set these to the parameters needed, the new version of this module switches it's parameters around
* supplying a new command with the new order while keeping the old /remove with the older order.
* /remove <nick> <channel> [reason ...]
* /fpart <channel> <nick> [reason ...]
*/
channame = parameters[ neworder ? 0 : 1];
username = parameters[ neworder ? 1 : 0];
/* Look up the user we're meant to be removing from the channel */
target = ServerInstance->FindNick(username);
/* And the channel we're meant to be removing them from */
channel = ServerInstance->FindChan(channame);
/* Fix by brain - someone needs to learn to validate their input! */
if (!target || !channel)
{
user->WriteServ("401 %s %s :No such nick/channel", user->nick, !target ? username : channame);
return CMD_FAILURE;
}
if (!channel->HasUser(target))
{
user->WriteServ( "NOTICE %s :*** The user %s is not on channel %s", user->nick, target->nick, channel->name);
return CMD_FAILURE;
}
/* This is adding support for the +q and +a channel modes, basically if they are enabled, and the remover has them set.
* Then we change the @|%|+ to & if they are +a, or ~ if they are +q */
protectkey = "cm_protect_" + std::string(channel->name);
founderkey = "cm_founder_" + std::string(channel->name);
if (ServerInstance->ULine(user->server) || ServerInstance->ULine(user->nick))
{
ulevel = chartolevel("U");
}
if (user->GetExt(founderkey))
{
ulevel = chartolevel("~");
}
else if (user->GetExt(protectkey))
{
ulevel = chartolevel("&");
}
else
{
ulevel = chartolevel(channel->GetPrefixChar(user));
}
/* Now it's the same idea, except for the target. If they're ulined make sure they get a higher level than the sender can */
if (ServerInstance->ULine(target->server) || ServerInstance->ULine(target->nick))
{
tlevel = chartolevel("U");
}
else if (target->GetExt(founderkey))
{
tlevel = chartolevel("~");
}
else if (target->GetExt(protectkey))
{
tlevel = chartolevel("&");
}
else
{
tlevel = chartolevel(channel->GetPrefixChar(target));
}
hasnokicks = (ServerInstance->FindModule("m_nokicks.so") && channel->IsModeSet('Q'));
/* We support the +Q channel mode via. the m_nokicks module, if the module is loaded and the mode is set then disallow the /remove */
if ((!IS_LOCAL(user)) || (!supportnokicks || !hasnokicks || (ulevel == ULINE)))
{
/* We'll let everyone remove their level and below, eg:
* ops can remove ops, halfops, voices, and those with no mode (no moders actually are set to 1)
* a ulined target will get a higher level than it's possible for a /remover to get..so they're safe.
* Nobody may remove a founder.
*/
if ((!IS_LOCAL(user)) || ((ulevel > PEON) && (ulevel >= tlevel) && (tlevel != OWNER)))
{
// no you can't just go from a std::ostringstream to a std::string, Om. -nenolod
// but you can do this, nenolod -brain
std::string reasonparam("No reason given");
/* If a reason is given, use it */
if(pcnt > 2)
{
/* Join params 2 ... pcnt - 1 (inclusive) into one */
irc::stringjoiner reason_join(" ", parameters, 2, pcnt - 1);
reasonparam = reason_join.GetJoined();
}
/* Build up the part reason string. */
reason = std::string("Removed by ") + user->nick + ": " + reasonparam;
channel->WriteChannelWithServ(ServerInstance->Config->ServerName, "NOTICE %s :%s removed %s from the channel", channel->name, user->nick, target->nick);
target->WriteServ("NOTICE %s :*** %s removed you from %s with the message: %s", target->nick, user->nick, channel->name, reasonparam.c_str());
if (!channel->PartUser(target, reason.c_str()))
delete channel;
}
else
{
user->WriteServ( "NOTICE %s :*** You do not have access to /remove %s from %s", user->nick, target->nick, channel->name);
return CMD_FAILURE;
}
}
else
{
/* m_nokicks.so was loaded and +Q was set, block! */
user->WriteServ( "484 %s %s :Can't remove user %s from channel (+Q set)", user->nick, channel->name, target->nick);
return CMD_FAILURE;
}
/* route me */
return CMD_SUCCESS;
}
};
/** Handle /REMOVE
*/
class cmd_remove : public command_t, public RemoveBase
{
public:
cmd_remove(InspIRCd* Instance, bool& snk) : command_t(Instance, "REMOVE", 0, 2), RemoveBase(Instance, snk)
{
this->source = "m_remove.so";
syntax = "<nick> <channel> [<reason>]";
}
CmdResult Handle (const char** parameters, int pcnt, userrec *user)
{
return RemoveBase::Handle(parameters, pcnt, user, false);
}
};
/** Handle /FPART
*/
class cmd_fpart : public command_t, public RemoveBase
{
public:
cmd_fpart(InspIRCd* Instance, bool& snk) : command_t(Instance, "FPART", 0, 2), RemoveBase(Instance, snk)
{
this->source = "m_remove.so";
syntax = "<channel> <nick> [<reason>]";
}
CmdResult Handle (const char** parameters, int pcnt, userrec *user)
{
return RemoveBase::Handle(parameters, pcnt, user, true);
}
};
class ModuleRemove : public Module
{
cmd_remove* mycommand;
cmd_fpart* mycommand2;
bool supportnokicks;
public:
ModuleRemove(InspIRCd* Me)
: Module(Me)
{
mycommand = new cmd_remove(ServerInstance, supportnokicks);
mycommand2 = new cmd_fpart(ServerInstance, supportnokicks);
ServerInstance->AddCommand(mycommand);
ServerInstance->AddCommand(mycommand2);
OnRehash(NULL,"");
}
void Implements(char* List)
{
List[I_On005Numeric] = List[I_OnRehash] = 1;
}
virtual void On005Numeric(std::string &output)
{
output.append(" REMOVE");
}
virtual void OnRehash(userrec* user, const std::string&)
{
ConfigReader conf(ServerInstance);
supportnokicks = conf.ReadFlag("remove", "supportnokicks", 0);
}
virtual ~ModuleRemove()
{
}
virtual Version GetVersion()
{
return Version(1,1,1,0,VF_VENDOR,API_VERSION);
}
};
MODULE_INIT(ModuleRemove)
\ No newline at end of file +/* +------------------------------------+ + * | 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 <sstream> +#include "users.h" +#include "channels.h" +#include "modules.h" +#include "configreader.h" + +/* $ModDesc: Provides a /remove command, this is mostly an alternative to /kick, except makes users appear to have parted the channel */ + +/* + * This module supports the use of the +q and +a usermodes, but should work without them too. + * Usage of the command is restricted to +hoaq, and you cannot remove a user with a "higher" level than yourself. + * eg: +h can remove +hv and users with no modes. +a can remove +aohv and users with no modes. +*/ + +/** Base class for /FPART and /REMOVE + */ +class RemoveBase +{ + private: + bool& supportnokicks; + InspIRCd* ServerInstance; + + protected: + RemoveBase(InspIRCd* Instance, bool& snk) : supportnokicks(snk), ServerInstance(Instance) + { + } + + enum ModeLevel { PEON = 0, HALFOP = 1, OP = 2, ADMIN = 3, OWNER = 4, ULINE = 5 }; + + /* This little function just converts a chanmode character (U ~ & @ & +) into an integer (5 4 3 2 1 0) */ + /* XXX - We should probably use the new mode prefix rank stuff + * for this instead now -- Brain */ + ModeLevel chartolevel(const std::string &privs) + { + if(privs.empty()) + { + return PEON; + } + + switch (privs[0]) + { + case 'U': + /* Ulined */ + return ULINE; + case '~': + /* Owner */ + return OWNER; + case '&': + /* Admin */ + return ADMIN; + case '@': + /* Operator */ + return OP; + case '%': + /* Halfop */ + return HALFOP; + default: + /* Peon */ + return PEON; + } + } + + CmdResult Handle (const char** parameters, int pcnt, userrec *user, bool neworder) + { + const char* channame; + const char* username; + userrec* target; + chanrec* channel; + ModeLevel tlevel; + ModeLevel ulevel; + std::string reason; + std::string protectkey; + std::string founderkey; + bool hasnokicks; + + /* Set these to the parameters needed, the new version of this module switches it's parameters around + * supplying a new command with the new order while keeping the old /remove with the older order. + * /remove <nick> <channel> [reason ...] + * /fpart <channel> <nick> [reason ...] + */ + channame = parameters[ neworder ? 0 : 1]; + username = parameters[ neworder ? 1 : 0]; + + /* Look up the user we're meant to be removing from the channel */ + target = ServerInstance->FindNick(username); + + /* And the channel we're meant to be removing them from */ + channel = ServerInstance->FindChan(channame); + + /* Fix by brain - someone needs to learn to validate their input! */ + if (!target || !channel) + { + user->WriteServ("401 %s %s :No such nick/channel", user->nick, !target ? username : channame); + return CMD_FAILURE; + } + + if (!channel->HasUser(target)) + { + user->WriteServ( "NOTICE %s :*** The user %s is not on channel %s", user->nick, target->nick, channel->name); + return CMD_FAILURE; + } + + /* This is adding support for the +q and +a channel modes, basically if they are enabled, and the remover has them set. + * Then we change the @|%|+ to & if they are +a, or ~ if they are +q */ + protectkey = "cm_protect_" + std::string(channel->name); + founderkey = "cm_founder_" + std::string(channel->name); + + if (ServerInstance->ULine(user->server) || ServerInstance->ULine(user->nick)) + { + ulevel = chartolevel("U"); + } + if (user->GetExt(founderkey)) + { + ulevel = chartolevel("~"); + } + else if (user->GetExt(protectkey)) + { + ulevel = chartolevel("&"); + } + else + { + ulevel = chartolevel(channel->GetPrefixChar(user)); + } + + /* Now it's the same idea, except for the target. If they're ulined make sure they get a higher level than the sender can */ + if (ServerInstance->ULine(target->server) || ServerInstance->ULine(target->nick)) + { + tlevel = chartolevel("U"); + } + else if (target->GetExt(founderkey)) + { + tlevel = chartolevel("~"); + } + else if (target->GetExt(protectkey)) + { + tlevel = chartolevel("&"); + } + else + { + tlevel = chartolevel(channel->GetPrefixChar(target)); + } + + hasnokicks = (ServerInstance->FindModule("m_nokicks.so") && channel->IsModeSet('Q')); + + /* We support the +Q channel mode via. the m_nokicks module, if the module is loaded and the mode is set then disallow the /remove */ + if ((!IS_LOCAL(user)) || (!supportnokicks || !hasnokicks || (ulevel == ULINE))) + { + /* We'll let everyone remove their level and below, eg: + * ops can remove ops, halfops, voices, and those with no mode (no moders actually are set to 1) + * a ulined target will get a higher level than it's possible for a /remover to get..so they're safe. + * Nobody may remove a founder. + */ + if ((!IS_LOCAL(user)) || ((ulevel > PEON) && (ulevel >= tlevel) && (tlevel != OWNER))) + { + // no you can't just go from a std::ostringstream to a std::string, Om. -nenolod + // but you can do this, nenolod -brain + + std::string reasonparam("No reason given"); + + /* If a reason is given, use it */ + if(pcnt > 2) + { + /* Join params 2 ... pcnt - 1 (inclusive) into one */ + irc::stringjoiner reason_join(" ", parameters, 2, pcnt - 1); + reasonparam = reason_join.GetJoined(); + } + + /* Build up the part reason string. */ + reason = std::string("Removed by ") + user->nick + ": " + reasonparam; + + channel->WriteChannelWithServ(ServerInstance->Config->ServerName, "NOTICE %s :%s removed %s from the channel", channel->name, user->nick, target->nick); + target->WriteServ("NOTICE %s :*** %s removed you from %s with the message: %s", target->nick, user->nick, channel->name, reasonparam.c_str()); + + if (!channel->PartUser(target, reason.c_str())) + delete channel; + } + else + { + user->WriteServ( "NOTICE %s :*** You do not have access to /remove %s from %s", user->nick, target->nick, channel->name); + return CMD_FAILURE; + } + } + else + { + /* m_nokicks.so was loaded and +Q was set, block! */ + user->WriteServ( "484 %s %s :Can't remove user %s from channel (+Q set)", user->nick, channel->name, target->nick); + return CMD_FAILURE; + } + + /* route me */ + return CMD_SUCCESS; + } +}; + +/** Handle /REMOVE + */ +class cmd_remove : public command_t, public RemoveBase +{ + public: + cmd_remove(InspIRCd* Instance, bool& snk) : command_t(Instance, "REMOVE", 0, 2), RemoveBase(Instance, snk) + { + this->source = "m_remove.so"; + syntax = "<nick> <channel> [<reason>]"; + } + + CmdResult Handle (const char** parameters, int pcnt, userrec *user) + { + return RemoveBase::Handle(parameters, pcnt, user, false); + } +}; + +/** Handle /FPART + */ +class cmd_fpart : public command_t, public RemoveBase +{ + public: + cmd_fpart(InspIRCd* Instance, bool& snk) : command_t(Instance, "FPART", 0, 2), RemoveBase(Instance, snk) + { + this->source = "m_remove.so"; + syntax = "<channel> <nick> [<reason>]"; + } + + CmdResult Handle (const char** parameters, int pcnt, userrec *user) + { + return RemoveBase::Handle(parameters, pcnt, user, true); + } +}; + +class ModuleRemove : public Module +{ + cmd_remove* mycommand; + cmd_fpart* mycommand2; + bool supportnokicks; + + + public: + ModuleRemove(InspIRCd* Me) + : Module(Me) + { + mycommand = new cmd_remove(ServerInstance, supportnokicks); + mycommand2 = new cmd_fpart(ServerInstance, supportnokicks); + ServerInstance->AddCommand(mycommand); + ServerInstance->AddCommand(mycommand2); + OnRehash(NULL,""); + } + + void Implements(char* List) + { + List[I_On005Numeric] = List[I_OnRehash] = 1; + } + + virtual void On005Numeric(std::string &output) + { + output.append(" REMOVE"); + } + + virtual void OnRehash(userrec* user, const std::string&) + { + ConfigReader conf(ServerInstance); + supportnokicks = conf.ReadFlag("remove", "supportnokicks", 0); + } + + virtual ~ModuleRemove() + { + } + + virtual Version GetVersion() + { + return Version(1,1,1,0,VF_VENDOR,API_VERSION); + } + +}; + +MODULE_INIT(ModuleRemove) diff --git a/src/modules/m_restrictbanned.cpp b/src/modules/m_restrictbanned.cpp index 5535ca464..9315385a1 100644 --- a/src/modules/m_restrictbanned.cpp +++ b/src/modules/m_restrictbanned.cpp @@ -1 +1,98 @@ -/* +------------------------------------+
* | 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 "users.h"
#include "channels.h"
#include "modules.h"
/* $ModDesc: Restricts banned users in a channel. May not speak, etc. */
class ModuleRestrictBanned : public Module
{
private:
public:
ModuleRestrictBanned(InspIRCd* Me) : Module(Me)
{
}
virtual ~ModuleRestrictBanned()
{
}
virtual Version GetVersion()
{
return Version(1,1,0,1,VF_VENDOR,API_VERSION);
}
void Implements(char* List)
{
List[I_OnLocalTopicChange] = List[I_OnUserPreNick] = List[I_OnUserPreNotice] = List[I_OnUserPreMessage] = 1;
}
int CheckRestricted(userrec *user, chanrec *channel, const std::string &action)
{
/* aren't local? we don't care. */
if (!IS_LOCAL(user))
return 0;
if (channel->GetStatus(user) < STATUS_VOICE && channel->IsBanned(user))
{
/* banned, boned. drop the message. */
user->WriteServ("NOTICE "+std::string(user->nick)+" :*** You may not " + action + ", as you are banned on channel " + channel->name);
return 1;
}
return 0;
}
virtual int OnUserPreNick(userrec *user, const std::string &newnick)
{
/* if they aren't local, we don't care */
if (!IS_LOCAL(user))
return 0;
/* bit of a special case. */
for (UCListIter i = user->chans.begin(); i != user->chans.end(); i++)
{
if (CheckRestricted(user, i->first, "change your nickname") == 1)
return 1;
}
return 0;
}
virtual int OnLocalTopicChange(userrec *user, chanrec *channel, const std::string &topic)
{
return CheckRestricted(user, channel, "change the topic");
}
virtual int OnUserPreMessage(userrec* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list)
{
return OnUserPreNotice(user,dest,target_type,text,status,exempt_list);
}
virtual int OnUserPreNotice(userrec* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list)
{
if (target_type == TYPE_CHANNEL)
{
chanrec *channel = (chanrec *)dest;
return CheckRestricted(user, channel, "message the channel");
}
return 0;
}
};
MODULE_INIT(ModuleRestrictBanned)
\ No newline at end of file +/* +------------------------------------+ + * | 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 "users.h" +#include "channels.h" +#include "modules.h" + +/* $ModDesc: Restricts banned users in a channel. May not speak, etc. */ + +class ModuleRestrictBanned : public Module +{ + private: + public: + ModuleRestrictBanned(InspIRCd* Me) : Module(Me) + { + } + + virtual ~ModuleRestrictBanned() + { + } + + virtual Version GetVersion() + { + return Version(1,1,0,1,VF_VENDOR,API_VERSION); + } + + void Implements(char* List) + { + List[I_OnLocalTopicChange] = List[I_OnUserPreNick] = List[I_OnUserPreNotice] = List[I_OnUserPreMessage] = 1; + } + + int CheckRestricted(userrec *user, chanrec *channel, const std::string &action) + { + /* aren't local? we don't care. */ + if (!IS_LOCAL(user)) + return 0; + + if (channel->GetStatus(user) < STATUS_VOICE && channel->IsBanned(user)) + { + /* banned, boned. drop the message. */ + user->WriteServ("NOTICE "+std::string(user->nick)+" :*** You may not " + action + ", as you are banned on channel " + channel->name); + return 1; + } + + return 0; + } + + virtual int OnUserPreNick(userrec *user, const std::string &newnick) + { + /* if they aren't local, we don't care */ + if (!IS_LOCAL(user)) + return 0; + + /* bit of a special case. */ + for (UCListIter i = user->chans.begin(); i != user->chans.end(); i++) + { + if (CheckRestricted(user, i->first, "change your nickname") == 1) + return 1; + } + + return 0; + } + + virtual int OnLocalTopicChange(userrec *user, chanrec *channel, const std::string &topic) + { + return CheckRestricted(user, channel, "change the topic"); + } + + virtual int OnUserPreMessage(userrec* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list) + { + return OnUserPreNotice(user,dest,target_type,text,status,exempt_list); + } + + virtual int OnUserPreNotice(userrec* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list) + { + if (target_type == TYPE_CHANNEL) + { + chanrec *channel = (chanrec *)dest; + + return CheckRestricted(user, channel, "message the channel"); + } + + return 0; + } +}; + +MODULE_INIT(ModuleRestrictBanned) diff --git a/src/modules/m_restrictchans.cpp b/src/modules/m_restrictchans.cpp index 1fc44f22b..1f2416d44 100644 --- a/src/modules/m_restrictchans.cpp +++ b/src/modules/m_restrictchans.cpp @@ -1 +1,85 @@ -/* +------------------------------------+
* | 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 "users.h"
#include "channels.h"
#include "modules.h"
/* $ModDesc: Only opers may create new channels if this module is loaded */
class ModuleRestrictChans : public Module
{
std::map<irc::string,int> allowchans;
void ReadConfig()
{
ConfigReader* MyConf = new ConfigReader(ServerInstance);
allowchans.clear();
for (int i = 0; i < MyConf->Enumerate("allowchannel"); i++)
{
std::string txt;
txt = MyConf->ReadValue("allowchannel", "name", i);
irc::string channel = txt.c_str();
allowchans[channel] = 1;
}
DELETE(MyConf);
}
public:
ModuleRestrictChans(InspIRCd* Me)
: Module(Me)
{
ReadConfig();
}
virtual void OnRehash(userrec* user, const std::string ¶meter)
{
ReadConfig();
}
void Implements(char* List)
{
List[I_OnUserPreJoin] = List[I_OnRehash] = 1;
}
virtual int OnUserPreJoin(userrec* user, chanrec* chan, const char* cname, std::string &privs)
{
irc::string x = cname;
// user is not an oper and its not in the allow list
if ((!IS_OPER(user)) && (allowchans.find(x) == allowchans.end()))
{
// channel does not yet exist (record is null, about to be created IF we were to allow it)
if (!chan)
{
user->WriteServ("530 %s %s :Only IRC operators may create new channels",user->nick,cname,cname);
return 1;
}
}
return 0;
}
virtual ~ModuleRestrictChans()
{
}
virtual Version GetVersion()
{
return Version(1,1,0,1,VF_VENDOR,API_VERSION);
}
};
MODULE_INIT(ModuleRestrictChans)
\ No newline at end of file +/* +------------------------------------+ + * | 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 "users.h" +#include "channels.h" +#include "modules.h" + +/* $ModDesc: Only opers may create new channels if this module is loaded */ + +class ModuleRestrictChans : public Module +{ + + + std::map<irc::string,int> allowchans; + + void ReadConfig() + { + ConfigReader* MyConf = new ConfigReader(ServerInstance); + allowchans.clear(); + for (int i = 0; i < MyConf->Enumerate("allowchannel"); i++) + { + std::string txt; + txt = MyConf->ReadValue("allowchannel", "name", i); + irc::string channel = txt.c_str(); + allowchans[channel] = 1; + } + DELETE(MyConf); + } + + public: + ModuleRestrictChans(InspIRCd* Me) + : Module(Me) + { + + ReadConfig(); + } + + virtual void OnRehash(userrec* user, const std::string ¶meter) + { + ReadConfig(); + } + + void Implements(char* List) + { + List[I_OnUserPreJoin] = List[I_OnRehash] = 1; + } + + virtual int OnUserPreJoin(userrec* user, chanrec* chan, const char* cname, std::string &privs) + { + irc::string x = cname; + // user is not an oper and its not in the allow list + if ((!IS_OPER(user)) && (allowchans.find(x) == allowchans.end())) + { + // channel does not yet exist (record is null, about to be created IF we were to allow it) + if (!chan) + { + user->WriteServ("530 %s %s :Only IRC operators may create new channels",user->nick,cname,cname); + return 1; + } + } + return 0; + } + + virtual ~ModuleRestrictChans() + { + } + + virtual Version GetVersion() + { + return Version(1,1,0,1,VF_VENDOR,API_VERSION); + } +}; + +MODULE_INIT(ModuleRestrictChans) diff --git a/src/modules/m_restrictmsg.cpp b/src/modules/m_restrictmsg.cpp index c05e320fe..0e95660b2 100644 --- a/src/modules/m_restrictmsg.cpp +++ b/src/modules/m_restrictmsg.cpp @@ -1 +1,75 @@ -/* +------------------------------------+
* | 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 "users.h"
#include "channels.h"
#include "modules.h"
/* $ModDesc: Forbids users from messaging each other. Users may still message opers and opers may message other opers. */
class ModuleRestrictMsg : public Module
{
public:
ModuleRestrictMsg(InspIRCd* Me)
: Module(Me)
{
}
void Implements(char* List)
{
List[I_OnUserPreMessage] = List[I_OnUserPreNotice] = 1;
}
virtual int OnUserPreMessage(userrec* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list)
{
if ((target_type == TYPE_USER) && (IS_LOCAL(user)))
{
userrec* u = (userrec*)dest;
// message allowed if:
// (1) the sender is opered
// (2) the recipient is opered
// anything else, blocked.
if (IS_OPER(u) || IS_OPER(user))
{
return 0;
}
user->WriteServ("531 %s %s :You are not permitted to send private messages to this user",user->nick,u->nick);
return 1;
}
// however, we must allow channel messages...
return 0;
}
virtual int OnUserPreNotice(userrec* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list)
{
return this->OnUserPreMessage(user,dest,target_type,text,status,exempt_list);
}
virtual ~ModuleRestrictMsg()
{
}
virtual Version GetVersion()
{
return Version(1,1,0,1,VF_VENDOR,API_VERSION);
}
};
MODULE_INIT(ModuleRestrictMsg)
\ No newline at end of file +/* +------------------------------------+ + * | 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 "users.h" +#include "channels.h" +#include "modules.h" + +/* $ModDesc: Forbids users from messaging each other. Users may still message opers and opers may message other opers. */ + + +class ModuleRestrictMsg : public Module +{ + + public: + + ModuleRestrictMsg(InspIRCd* Me) + : Module(Me) + { + + } + + void Implements(char* List) + { + List[I_OnUserPreMessage] = List[I_OnUserPreNotice] = 1; + } + + virtual int OnUserPreMessage(userrec* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list) + { + if ((target_type == TYPE_USER) && (IS_LOCAL(user))) + { + userrec* u = (userrec*)dest; + + // message allowed if: + // (1) the sender is opered + // (2) the recipient is opered + // anything else, blocked. + if (IS_OPER(u) || IS_OPER(user)) + { + return 0; + } + user->WriteServ("531 %s %s :You are not permitted to send private messages to this user",user->nick,u->nick); + return 1; + } + + // however, we must allow channel messages... + return 0; + } + + virtual int OnUserPreNotice(userrec* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list) + { + return this->OnUserPreMessage(user,dest,target_type,text,status,exempt_list); + } + + virtual ~ModuleRestrictMsg() + { + } + + virtual Version GetVersion() + { + return Version(1,1,0,1,VF_VENDOR,API_VERSION); + } +}; + +MODULE_INIT(ModuleRestrictMsg) diff --git a/src/modules/m_safelist.cpp b/src/modules/m_safelist.cpp index abd782c86..4adfc0011 100644 --- a/src/modules/m_safelist.cpp +++ b/src/modules/m_safelist.cpp @@ -1 +1,268 @@ -/* +------------------------------------+
* | 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 "users.h"
#include "channels.h"
#include "modules.h"
#include "wildcard.h"
/** Holds a users m_safelist state
*/
class ListData : public classbase
{
public:
long list_start;
long list_position;
bool list_ended;
const std::string glob;
int minusers;
int maxusers;
ListData() : list_start(0), list_position(0), list_ended(false) {};
ListData(long pos, time_t t, const std::string &pattern, int mi, int ma) : list_start(t), list_position(pos), list_ended(false), glob(pattern), minusers(mi), maxusers(ma) {};
};
/* $ModDesc: A module overriding /list, and making it safe - stop those sendq problems. */
class ModuleSafeList : public Module
{
time_t ThrottleSecs;
size_t ServerNameSize;
int global_listing;
int LimitList;
public:
ModuleSafeList(InspIRCd* Me) : Module(Me)
{
OnRehash(NULL, "");
}
virtual ~ModuleSafeList()
{
}
virtual void OnRehash(userrec* user, const std::string ¶meter)
{
ConfigReader MyConf(ServerInstance);
ThrottleSecs = MyConf.ReadInteger("safelist", "throttle", "60", 0, true);
LimitList = MyConf.ReadInteger("safelist", "maxlisters", "50", 0, true);
ServerNameSize = strlen(ServerInstance->Config->ServerName) + 4;
global_listing = 0;
}
virtual Version GetVersion()
{
return Version(1,1,0,0,VF_VENDOR,API_VERSION);
}
void Implements(char* List)
{
List[I_OnBufferFlushed] = List[I_OnPreCommand] = List[I_OnCleanup] = List[I_OnUserQuit] = List[I_On005Numeric] = List[I_OnRehash] = 1;
}
/*
* OnPreCommand()
* Intercept the LIST command.
*/
virtual int OnPreCommand(const std::string &command, const char** parameters, int pcnt, userrec *user, bool validated, const std::string &original_line)
{
/* If the command doesnt appear to be valid, we dont want to mess with it. */
if (!validated)
return 0;
if (command == "LIST")
{
return this->HandleList(parameters, pcnt, user);
}
return 0;
}
/*
* HandleList()
* Handle (override) the LIST command.
*/
int HandleList(const char** parameters, int pcnt, userrec* user)
{
int minusers = 0, maxusers = 0;
if (global_listing >= LimitList)
{
user->WriteServ("NOTICE %s :*** Server load is currently too heavy. Please try again later.", user->nick);
user->WriteServ("321 %s Channel :Users Name",user->nick);
user->WriteServ("323 %s :End of channel list.",user->nick);
return 1;
}
/* First, let's check if the user is currently /list'ing */
ListData *ld;
user->GetExt("safelist_cache", ld);
if (ld)
{
/* user is already /list'ing, we don't want to do shit. */
return 1;
}
/* Work around mIRC suckyness. YOU SUCK, KHALED! */
if (pcnt == 1)
{
if (*parameters[0] == '<')
{
maxusers = atoi(parameters[0]+1);
ServerInstance->Log(DEBUG,"Max users: %d", maxusers);
pcnt = 0;
}
else if (*parameters[0] == '>')
{
minusers = atoi(parameters[0]+1);
ServerInstance->Log(DEBUG,"Min users: %d", minusers);
pcnt = 0;
}
}
time_t* last_list_time;
user->GetExt("safelist_last", last_list_time);
if (last_list_time)
{
if (ServerInstance->Time() < (*last_list_time)+ThrottleSecs)
{
user->WriteServ("NOTICE %s :*** Woah there, slow down a little, you can't /LIST so often!",user->nick);
user->WriteServ("321 %s Channel :Users Name",user->nick);
user->WriteServ("323 %s :End of channel list.",user->nick);
return 1;
}
DELETE(last_list_time);
user->Shrink("safelist_last");
}
/*
* start at channel 0! ;)
*/
ld = new ListData(0,ServerInstance->Time(), pcnt ? parameters[0] : "*", minusers, maxusers);
user->Extend("safelist_cache", ld);
time_t* llt = new time_t;
*llt = ServerInstance->Time();
user->Extend("safelist_last", llt);
user->WriteServ("321 %s Channel :Users Name",user->nick);
global_listing++;
return 1;
}
virtual void OnBufferFlushed(userrec* user)
{
char buffer[MAXBUF];
ListData* ld;
if (user->GetExt("safelist_cache", ld))
{
chanrec* chan = NULL;
long amount_sent = 0;
do
{
chan = ServerInstance->GetChannelIndex(ld->list_position);
bool has_user = (chan && chan->HasUser(user));
long users = chan ? chan->GetUserCounter() : 0;
bool too_few = (ld->minusers && (users <= ld->minusers));
bool too_many = (ld->maxusers && (users >= ld->maxusers));
if (chan && (too_many || too_few))
{
ld->list_position++;
continue;
}
if ((chan) && (chan->modes[CM_PRIVATE]))
{
bool display = (match(chan->name, ld->glob.c_str()) || (*chan->topic && match(chan->topic, ld->glob.c_str())));
if ((users) && (display))
{
int counter = snprintf(buffer, MAXBUF, "322 %s *", user->nick);
amount_sent += counter + ServerNameSize;
user->WriteServ(std::string(buffer));
}
}
else if ((chan) && (((!(chan->modes[CM_PRIVATE])) && (!(chan->modes[CM_SECRET]))) || (has_user)))
{
bool display = (match(chan->name, ld->glob.c_str()) || (*chan->topic && match(chan->topic, ld->glob.c_str())));
if ((users) && (display))
{
int counter = snprintf(buffer, MAXBUF, "322 %s %s %ld :[+%s] %s",user->nick, chan->name, users, chan->ChanModes(has_user), chan->topic);
amount_sent += counter + ServerNameSize;
user->WriteServ(std::string(buffer));
}
}
else
{
if (!chan)
{
if (!ld->list_ended)
{
ld->list_ended = true;
user->WriteServ("323 %s :End of channel list.",user->nick);
}
}
}
ld->list_position++;
}
while ((chan != NULL) && (amount_sent < (user->sendqmax / 4)));
if (ld->list_ended)
{
user->Shrink("safelist_cache");
DELETE(ld);
global_listing--;
}
}
}
virtual void OnCleanup(int target_type, void* item)
{
if(target_type == TYPE_USER)
{
userrec* u = (userrec*)item;
ListData* ld;
u->GetExt("safelist_cache", ld);
if (ld)
{
u->Shrink("safelist_cache");
DELETE(ld);
global_listing--;
}
time_t* last_list_time;
u->GetExt("safelist_last", last_list_time);
if (last_list_time)
{
DELETE(last_list_time);
u->Shrink("safelist_last");
}
}
}
virtual void On005Numeric(std::string &output)
{
output.append(" SAFELIST");
}
virtual void OnUserQuit(userrec* user, const std::string &message, const std::string &oper_message)
{
this->OnCleanup(TYPE_USER,user);
}
};
MODULE_INIT(ModuleSafeList)
\ No newline at end of file +/* +------------------------------------+ + * | 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 "users.h" +#include "channels.h" +#include "modules.h" +#include "wildcard.h" + +/** Holds a users m_safelist state + */ +class ListData : public classbase +{ + public: + long list_start; + long list_position; + bool list_ended; + const std::string glob; + int minusers; + int maxusers; + + ListData() : list_start(0), list_position(0), list_ended(false) {}; + ListData(long pos, time_t t, const std::string &pattern, int mi, int ma) : list_start(t), list_position(pos), list_ended(false), glob(pattern), minusers(mi), maxusers(ma) {}; +}; + +/* $ModDesc: A module overriding /list, and making it safe - stop those sendq problems. */ + +class ModuleSafeList : public Module +{ + time_t ThrottleSecs; + size_t ServerNameSize; + int global_listing; + int LimitList; + public: + ModuleSafeList(InspIRCd* Me) : Module(Me) + { + OnRehash(NULL, ""); + } + + virtual ~ModuleSafeList() + { + } + + virtual void OnRehash(userrec* user, const std::string ¶meter) + { + ConfigReader MyConf(ServerInstance); + ThrottleSecs = MyConf.ReadInteger("safelist", "throttle", "60", 0, true); + LimitList = MyConf.ReadInteger("safelist", "maxlisters", "50", 0, true); + ServerNameSize = strlen(ServerInstance->Config->ServerName) + 4; + global_listing = 0; + } + + virtual Version GetVersion() + { + return Version(1,1,0,0,VF_VENDOR,API_VERSION); + } + + void Implements(char* List) + { + List[I_OnBufferFlushed] = List[I_OnPreCommand] = List[I_OnCleanup] = List[I_OnUserQuit] = List[I_On005Numeric] = List[I_OnRehash] = 1; + } + + /* + * OnPreCommand() + * Intercept the LIST command. + */ + virtual int OnPreCommand(const std::string &command, const char** parameters, int pcnt, userrec *user, bool validated, const std::string &original_line) + { + /* If the command doesnt appear to be valid, we dont want to mess with it. */ + if (!validated) + return 0; + + if (command == "LIST") + { + return this->HandleList(parameters, pcnt, user); + } + return 0; + } + + /* + * HandleList() + * Handle (override) the LIST command. + */ + int HandleList(const char** parameters, int pcnt, userrec* user) + { + int minusers = 0, maxusers = 0; + + if (global_listing >= LimitList) + { + user->WriteServ("NOTICE %s :*** Server load is currently too heavy. Please try again later.", user->nick); + user->WriteServ("321 %s Channel :Users Name",user->nick); + user->WriteServ("323 %s :End of channel list.",user->nick); + return 1; + } + + /* First, let's check if the user is currently /list'ing */ + ListData *ld; + user->GetExt("safelist_cache", ld); + + if (ld) + { + /* user is already /list'ing, we don't want to do shit. */ + return 1; + } + + /* Work around mIRC suckyness. YOU SUCK, KHALED! */ + if (pcnt == 1) + { + if (*parameters[0] == '<') + { + maxusers = atoi(parameters[0]+1); + ServerInstance->Log(DEBUG,"Max users: %d", maxusers); + pcnt = 0; + } + else if (*parameters[0] == '>') + { + minusers = atoi(parameters[0]+1); + ServerInstance->Log(DEBUG,"Min users: %d", minusers); + pcnt = 0; + } + } + + time_t* last_list_time; + user->GetExt("safelist_last", last_list_time); + if (last_list_time) + { + if (ServerInstance->Time() < (*last_list_time)+ThrottleSecs) + { + user->WriteServ("NOTICE %s :*** Woah there, slow down a little, you can't /LIST so often!",user->nick); + user->WriteServ("321 %s Channel :Users Name",user->nick); + user->WriteServ("323 %s :End of channel list.",user->nick); + return 1; + } + + DELETE(last_list_time); + user->Shrink("safelist_last"); + } + + + /* + * start at channel 0! ;) + */ + ld = new ListData(0,ServerInstance->Time(), pcnt ? parameters[0] : "*", minusers, maxusers); + user->Extend("safelist_cache", ld); + + time_t* llt = new time_t; + *llt = ServerInstance->Time(); + user->Extend("safelist_last", llt); + + user->WriteServ("321 %s Channel :Users Name",user->nick); + + global_listing++; + + return 1; + } + + virtual void OnBufferFlushed(userrec* user) + { + char buffer[MAXBUF]; + ListData* ld; + if (user->GetExt("safelist_cache", ld)) + { + chanrec* chan = NULL; + long amount_sent = 0; + do + { + chan = ServerInstance->GetChannelIndex(ld->list_position); + bool has_user = (chan && chan->HasUser(user)); + long users = chan ? chan->GetUserCounter() : 0; + + bool too_few = (ld->minusers && (users <= ld->minusers)); + bool too_many = (ld->maxusers && (users >= ld->maxusers)); + + if (chan && (too_many || too_few)) + { + ld->list_position++; + continue; + } + + if ((chan) && (chan->modes[CM_PRIVATE])) + { + bool display = (match(chan->name, ld->glob.c_str()) || (*chan->topic && match(chan->topic, ld->glob.c_str()))); + if ((users) && (display)) + { + int counter = snprintf(buffer, MAXBUF, "322 %s *", user->nick); + amount_sent += counter + ServerNameSize; + user->WriteServ(std::string(buffer)); + } + } + else if ((chan) && (((!(chan->modes[CM_PRIVATE])) && (!(chan->modes[CM_SECRET]))) || (has_user))) + { + bool display = (match(chan->name, ld->glob.c_str()) || (*chan->topic && match(chan->topic, ld->glob.c_str()))); + if ((users) && (display)) + { + int counter = snprintf(buffer, MAXBUF, "322 %s %s %ld :[+%s] %s",user->nick, chan->name, users, chan->ChanModes(has_user), chan->topic); + amount_sent += counter + ServerNameSize; + user->WriteServ(std::string(buffer)); + } + } + else + { + if (!chan) + { + if (!ld->list_ended) + { + ld->list_ended = true; + user->WriteServ("323 %s :End of channel list.",user->nick); + } + } + } + ld->list_position++; + } + while ((chan != NULL) && (amount_sent < (user->sendqmax / 4))); + if (ld->list_ended) + { + user->Shrink("safelist_cache"); + DELETE(ld); + global_listing--; + } + } + } + + virtual void OnCleanup(int target_type, void* item) + { + if(target_type == TYPE_USER) + { + userrec* u = (userrec*)item; + ListData* ld; + u->GetExt("safelist_cache", ld); + if (ld) + { + u->Shrink("safelist_cache"); + DELETE(ld); + global_listing--; + } + time_t* last_list_time; + u->GetExt("safelist_last", last_list_time); + if (last_list_time) + { + DELETE(last_list_time); + u->Shrink("safelist_last"); + } + } + } + + virtual void On005Numeric(std::string &output) + { + output.append(" SAFELIST"); + } + + virtual void OnUserQuit(userrec* user, const std::string &message, const std::string &oper_message) + { + this->OnCleanup(TYPE_USER,user); + } + +}; + +MODULE_INIT(ModuleSafeList) diff --git a/src/modules/m_sajoin.cpp b/src/modules/m_sajoin.cpp index 2b143606f..0c9822fb9 100644 --- a/src/modules/m_sajoin.cpp +++ b/src/modules/m_sajoin.cpp @@ -1 +1,114 @@ -/* +------------------------------------+
* | 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 "users.h"
#include "channels.h"
#include "modules.h"
/* $ModDesc: Provides support for unreal-style SAJOIN command */
/** Handle /SAJOIN
*/
class cmd_sajoin : public command_t
{
public:
cmd_sajoin (InspIRCd* Instance) : command_t(Instance,"SAJOIN", 'o', 2)
{
this->source = "m_sajoin.so";
syntax = "<nick> <channel>";
}
CmdResult Handle (const char** parameters, int pcnt, userrec *user)
{
userrec* dest = ServerInstance->FindNick(parameters[0]);
if (dest)
{
if (ServerInstance->ULine(dest->server))
{
user->WriteServ("990 %s :Cannot use an SA command on a u-lined client",user->nick);
return CMD_FAILURE;
}
if (!ServerInstance->IsChannel(parameters[1]))
{
/* we didn't need to check this for each character ;) */
user->WriteServ("NOTICE "+std::string(user->nick)+" :*** Invalid characters in channel name");
return CMD_FAILURE;
}
/* For local users, we send the JoinUser which may create a channel and set its TS.
* For non-local users, we just return CMD_SUCCESS, knowing this will propogate it where it needs to be
* and then that server will generate the users JOIN or FJOIN instead.
*/
if (IS_LOCAL(dest))
{
chanrec::JoinUser(ServerInstance, dest, parameters[1], true, "", ServerInstance->Time(true));
/* Fix for dotslasher and w00t - if the join didnt succeed, return CMD_FAILURE so that it doesnt propogate */
chanrec* n = ServerInstance->FindChan(parameters[1]);
if (n)
{
if (n->HasUser(dest))
{
ServerInstance->WriteOpers("*** "+std::string(user->nick)+" used SAJOIN to make "+std::string(dest->nick)+" join "+parameters[1]);
return CMD_SUCCESS;
}
else
{
user->WriteServ("NOTICE "+std::string(user->nick)+" :*** Could not join "+std::string(dest->nick)+" to "+parameters[1]+" (User is probably banned, or blocking modes)");
return CMD_FAILURE;
}
}
else
{
user->WriteServ("NOTICE "+std::string(user->nick)+" :*** Could not join "+std::string(dest->nick)+" to "+parameters[1]);
return CMD_FAILURE;
}
}
else
{
ServerInstance->WriteOpers("*** "+std::string(user->nick)+" sent remote SAJOIN to make "+std::string(dest->nick)+" join "+parameters[1]);
return CMD_SUCCESS;
}
}
else
{
user->WriteServ("NOTICE "+std::string(user->nick)+" :*** No such nickname "+parameters[0]);
return CMD_FAILURE;
}
}
};
class ModuleSajoin : public Module
{
cmd_sajoin* mycommand;
public:
ModuleSajoin(InspIRCd* Me)
: Module(Me)
{
mycommand = new cmd_sajoin(ServerInstance);
ServerInstance->AddCommand(mycommand);
}
virtual ~ModuleSajoin()
{
}
virtual Version GetVersion()
{
return Version(1,1,0,1,VF_VENDOR,API_VERSION);
}
};
MODULE_INIT(ModuleSajoin)
\ No newline at end of file +/* +------------------------------------+ + * | 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 "users.h" +#include "channels.h" +#include "modules.h" + +/* $ModDesc: Provides support for unreal-style SAJOIN command */ + +/** Handle /SAJOIN + */ +class cmd_sajoin : public command_t +{ + public: + cmd_sajoin (InspIRCd* Instance) : command_t(Instance,"SAJOIN", 'o', 2) + { + this->source = "m_sajoin.so"; + syntax = "<nick> <channel>"; + } + + CmdResult Handle (const char** parameters, int pcnt, userrec *user) + { + userrec* dest = ServerInstance->FindNick(parameters[0]); + if (dest) + { + if (ServerInstance->ULine(dest->server)) + { + user->WriteServ("990 %s :Cannot use an SA command on a u-lined client",user->nick); + return CMD_FAILURE; + } + if (!ServerInstance->IsChannel(parameters[1])) + { + /* we didn't need to check this for each character ;) */ + user->WriteServ("NOTICE "+std::string(user->nick)+" :*** Invalid characters in channel name"); + return CMD_FAILURE; + } + + /* For local users, we send the JoinUser which may create a channel and set its TS. + * For non-local users, we just return CMD_SUCCESS, knowing this will propogate it where it needs to be + * and then that server will generate the users JOIN or FJOIN instead. + */ + if (IS_LOCAL(dest)) + { + chanrec::JoinUser(ServerInstance, dest, parameters[1], true, "", ServerInstance->Time(true)); + /* Fix for dotslasher and w00t - if the join didnt succeed, return CMD_FAILURE so that it doesnt propogate */ + chanrec* n = ServerInstance->FindChan(parameters[1]); + if (n) + { + if (n->HasUser(dest)) + { + ServerInstance->WriteOpers("*** "+std::string(user->nick)+" used SAJOIN to make "+std::string(dest->nick)+" join "+parameters[1]); + return CMD_SUCCESS; + } + else + { + user->WriteServ("NOTICE "+std::string(user->nick)+" :*** Could not join "+std::string(dest->nick)+" to "+parameters[1]+" (User is probably banned, or blocking modes)"); + return CMD_FAILURE; + } + } + else + { + user->WriteServ("NOTICE "+std::string(user->nick)+" :*** Could not join "+std::string(dest->nick)+" to "+parameters[1]); + return CMD_FAILURE; + } + } + else + { + ServerInstance->WriteOpers("*** "+std::string(user->nick)+" sent remote SAJOIN to make "+std::string(dest->nick)+" join "+parameters[1]); + return CMD_SUCCESS; + } + } + else + { + user->WriteServ("NOTICE "+std::string(user->nick)+" :*** No such nickname "+parameters[0]); + return CMD_FAILURE; + } + } +}; + +class ModuleSajoin : public Module +{ + cmd_sajoin* mycommand; + public: + ModuleSajoin(InspIRCd* Me) + : Module(Me) + { + + mycommand = new cmd_sajoin(ServerInstance); + ServerInstance->AddCommand(mycommand); + } + + virtual ~ModuleSajoin() + { + } + + virtual Version GetVersion() + { + return Version(1,1,0,1,VF_VENDOR,API_VERSION); + } + +}; + +MODULE_INIT(ModuleSajoin) diff --git a/src/modules/m_samode.cpp b/src/modules/m_samode.cpp index f48e078b1..88d0d666e 100644 --- a/src/modules/m_samode.cpp +++ b/src/modules/m_samode.cpp @@ -1 +1,98 @@ -/* +------------------------------------+
* | 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.
*
* ---------------------------------------------------
*/
/* $ModDesc: Provides more advanced UnrealIRCd SAMODE command */
#include "inspircd.h"
#include "users.h"
#include "channels.h"
#include "modules.h"
/** Handle /SAMODE
*/
class cmd_samode : public command_t
{
public:
cmd_samode (InspIRCd* Instance) : command_t(Instance,"SAMODE", 'o', 2)
{
this->source = "m_samode.so";
syntax = "<target> <modes> {<mode-parameters>}";
}
CmdResult Handle (const char** parameters, int pcnt, userrec *user)
{
/*
* Handles an SAMODE request. Notifies all +s users.
*/
userrec* n = new userrec(ServerInstance);
n->SetFd(FD_MAGIC_NUMBER);
ServerInstance->SendMode(parameters,pcnt,n);
delete n;
if (ServerInstance->Modes->GetLastParse().length())
{
ServerInstance->WriteOpers("*** " + std::string(user->nick) + " used SAMODE: " + ServerInstance->Modes->GetLastParse());
std::deque<std::string> n;
irc::spacesepstream spaced(ServerInstance->Modes->GetLastParse());
std::string one = "*";
while ((one = spaced.GetToken()) != "")
n.push_back(one);
Event rmode((char *)&n, NULL, "send_mode");
rmode.Send(ServerInstance);
n.clear();
n.push_back(std::string(user->nick) + " used SAMODE: " + ServerInstance->Modes->GetLastParse());
Event rmode2((char *)&n, NULL, "send_opers");
rmode2.Send(ServerInstance);
/* XXX: Yes, this is right. We dont want to propogate the
* actual SAMODE command, just the MODE command generated
* by the send_mode
*/
return CMD_LOCALONLY;
}
else
{
user->WriteServ("NOTICE %s :*** Invalid SAMODE sequence.", user->nick);
}
return CMD_FAILURE;
}
};
class ModuleSaMode : public Module
{
cmd_samode* mycommand;
public:
ModuleSaMode(InspIRCd* Me)
: Module(Me)
{
mycommand = new cmd_samode(ServerInstance);
ServerInstance->AddCommand(mycommand);
}
virtual ~ModuleSaMode()
{
}
virtual Version GetVersion()
{
return Version(1,1,2,2,VF_VENDOR,API_VERSION);
}
};
MODULE_INIT(ModuleSaMode)
\ No newline at end of file +/* +------------------------------------+ + * | 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. + * + * --------------------------------------------------- + */ + +/* $ModDesc: Provides more advanced UnrealIRCd SAMODE command */ + +#include "inspircd.h" +#include "users.h" +#include "channels.h" +#include "modules.h" + +/** Handle /SAMODE + */ +class cmd_samode : public command_t +{ + public: + cmd_samode (InspIRCd* Instance) : command_t(Instance,"SAMODE", 'o', 2) + { + this->source = "m_samode.so"; + syntax = "<target> <modes> {<mode-parameters>}"; + } + + CmdResult Handle (const char** parameters, int pcnt, userrec *user) + { + /* + * Handles an SAMODE request. Notifies all +s users. + */ + + userrec* n = new userrec(ServerInstance); + n->SetFd(FD_MAGIC_NUMBER); + ServerInstance->SendMode(parameters,pcnt,n); + delete n; + + if (ServerInstance->Modes->GetLastParse().length()) + { + ServerInstance->WriteOpers("*** " + std::string(user->nick) + " used SAMODE: " + ServerInstance->Modes->GetLastParse()); + + std::deque<std::string> n; + irc::spacesepstream spaced(ServerInstance->Modes->GetLastParse()); + std::string one = "*"; + while ((one = spaced.GetToken()) != "") + n.push_back(one); + + Event rmode((char *)&n, NULL, "send_mode"); + rmode.Send(ServerInstance); + + n.clear(); + n.push_back(std::string(user->nick) + " used SAMODE: " + ServerInstance->Modes->GetLastParse()); + Event rmode2((char *)&n, NULL, "send_opers"); + rmode2.Send(ServerInstance); + + /* XXX: Yes, this is right. We dont want to propogate the + * actual SAMODE command, just the MODE command generated + * by the send_mode + */ + return CMD_LOCALONLY; + } + else + { + user->WriteServ("NOTICE %s :*** Invalid SAMODE sequence.", user->nick); + } + + return CMD_FAILURE; + } +}; + +class ModuleSaMode : public Module +{ + cmd_samode* mycommand; + public: + ModuleSaMode(InspIRCd* Me) + : Module(Me) + { + + mycommand = new cmd_samode(ServerInstance); + ServerInstance->AddCommand(mycommand); + } + + virtual ~ModuleSaMode() + { + } + + virtual Version GetVersion() + { + return Version(1,1,2,2,VF_VENDOR,API_VERSION); + } +}; + +MODULE_INIT(ModuleSaMode) diff --git a/src/modules/m_sanick.cpp b/src/modules/m_sanick.cpp index 8810550ae..715d978c3 100644 --- a/src/modules/m_sanick.cpp +++ b/src/modules/m_sanick.cpp @@ -1 +1,97 @@ -/* +------------------------------------+
* | 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 "users.h"
#include "channels.h"
#include "modules.h"
/* $ModDesc: Provides support for SANICK command */
/** Handle /SANICK
*/
class cmd_sanick : public command_t
{
public:
cmd_sanick (InspIRCd* Instance) : command_t(Instance,"SANICK", 'o', 2)
{
this->source = "m_sanick.so";
syntax = "<nick> <new-nick>";
}
CmdResult Handle (const char** parameters, int pcnt, userrec *user)
{
userrec* source = ServerInstance->FindNick(parameters[0]);
if (source)
{
if (ServerInstance->ULine(source->server))
{
user->WriteServ("990 %s :Cannot use an SA command on a u-lined client",user->nick);
return CMD_FAILURE;
}
std::string oldnick = user->nick;
if (ServerInstance->IsNick(parameters[1]))
{
if (source->ForceNickChange(parameters[1]))
{
ServerInstance->WriteOpers("*** " + oldnick+" used SANICK to change "+std::string(parameters[0])+" to "+parameters[1]);
return CMD_SUCCESS;
}
else
{
/* We couldnt change the nick */
ServerInstance->WriteOpers("*** " + oldnick+" failed SANICK (from "+std::string(parameters[0])+" to "+parameters[1]+")");
return CMD_FAILURE;
}
}
else
{
user->WriteServ("NOTICE %s :*** Invalid nickname '%s'", user->nick, parameters[1]);
}
return CMD_FAILURE;
}
else
{
user->WriteServ("NOTICE %s :*** No such nickname: '%s'", user->nick, parameters[0]);
}
return CMD_FAILURE;
}
};
class ModuleSanick : public Module
{
cmd_sanick* mycommand;
public:
ModuleSanick(InspIRCd* Me)
: Module(Me)
{
mycommand = new cmd_sanick(ServerInstance);
ServerInstance->AddCommand(mycommand);
}
virtual ~ModuleSanick()
{
}
virtual Version GetVersion()
{
return Version(1,1,0,1,VF_VENDOR,API_VERSION);
}
};
MODULE_INIT(ModuleSanick)
\ No newline at end of file +/* +------------------------------------+ + * | 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 "users.h" +#include "channels.h" +#include "modules.h" + +/* $ModDesc: Provides support for SANICK command */ + +/** Handle /SANICK + */ +class cmd_sanick : public command_t +{ + public: + cmd_sanick (InspIRCd* Instance) : command_t(Instance,"SANICK", 'o', 2) + { + this->source = "m_sanick.so"; + syntax = "<nick> <new-nick>"; + } + + CmdResult Handle (const char** parameters, int pcnt, userrec *user) + { + userrec* source = ServerInstance->FindNick(parameters[0]); + if (source) + { + if (ServerInstance->ULine(source->server)) + { + user->WriteServ("990 %s :Cannot use an SA command on a u-lined client",user->nick); + return CMD_FAILURE; + } + std::string oldnick = user->nick; + if (ServerInstance->IsNick(parameters[1])) + { + if (source->ForceNickChange(parameters[1])) + { + ServerInstance->WriteOpers("*** " + oldnick+" used SANICK to change "+std::string(parameters[0])+" to "+parameters[1]); + return CMD_SUCCESS; + } + else + { + /* We couldnt change the nick */ + ServerInstance->WriteOpers("*** " + oldnick+" failed SANICK (from "+std::string(parameters[0])+" to "+parameters[1]+")"); + return CMD_FAILURE; + } + } + else + { + user->WriteServ("NOTICE %s :*** Invalid nickname '%s'", user->nick, parameters[1]); + } + + return CMD_FAILURE; + } + else + { + user->WriteServ("NOTICE %s :*** No such nickname: '%s'", user->nick, parameters[0]); + } + + return CMD_FAILURE; + } +}; + + +class ModuleSanick : public Module +{ + cmd_sanick* mycommand; + public: + ModuleSanick(InspIRCd* Me) + : Module(Me) + { + + mycommand = new cmd_sanick(ServerInstance); + ServerInstance->AddCommand(mycommand); + } + + virtual ~ModuleSanick() + { + } + + virtual Version GetVersion() + { + return Version(1,1,0,1,VF_VENDOR,API_VERSION); + } + +}; + +MODULE_INIT(ModuleSanick) diff --git a/src/modules/m_sapart.cpp b/src/modules/m_sapart.cpp index 4d663e822..829607e58 100644 --- a/src/modules/m_sapart.cpp +++ b/src/modules/m_sapart.cpp @@ -1 +1,113 @@ -/* +------------------------------------+
* | 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 "users.h"
#include "channels.h"
#include "modules.h"
/* $ModDesc: Provides support for unreal-style SAPART command */
/** Handle /SAPART
*/
class cmd_sapart : public command_t
{
public:
cmd_sapart (InspIRCd* Instance) : command_t(Instance,"SAPART", 'o', 2)
{
this->source = "m_sapart.so";
syntax = "<nick> <channel>";
}
CmdResult Handle (const char** parameters, int pcnt, userrec *user)
{
userrec* dest = ServerInstance->FindNick(parameters[0]);
chanrec* channel = ServerInstance->FindChan(parameters[1]);
if (dest && channel)
{
if (ServerInstance->ULine(dest->server))
{
user->WriteServ("990 %s :Cannot use an SA command on a u-lined client",user->nick);
return CMD_FAILURE;
}
/* For local clients, directly part them generating a PART message. For remote clients,
* just return CMD_SUCCESS knowing the protocol module will route the SAPART to the users
* local server and that will generate the PART instead
*/
if (IS_LOCAL(dest))
{
if (!channel->PartUser(dest, dest->nick))
delete channel;
chanrec* n = ServerInstance->FindChan(parameters[1]);
if (!n)
{
ServerInstance->WriteOpers("*** "+std::string(user->nick)+" used SAPART to make "+dest->nick+" part "+parameters[1]);
return CMD_SUCCESS;
}
else
{
if (!n->HasUser(dest))
{
ServerInstance->WriteOpers("*** "+std::string(user->nick)+" used SAPART to make "+dest->nick+" part "+parameters[1]);
return CMD_SUCCESS;
}
else
{
user->WriteServ("NOTICE %s :*** Unable to make %s part %s",user->nick, dest->nick, parameters[1]);
return CMD_FAILURE;
}
}
}
else
{
ServerInstance->WriteOpers("*** "+std::string(user->nick)+" sent remote SAPART to make "+dest->nick+" part "+parameters[1]);
}
return CMD_SUCCESS;
}
else
{
user->WriteServ("NOTICE %s :*** Invalid nickname or channel", user->nick);
}
return CMD_FAILURE;
}
};
class ModuleSapart : public Module
{
cmd_sapart* mycommand;
public:
ModuleSapart(InspIRCd* Me)
: Module(Me)
{
mycommand = new cmd_sapart(ServerInstance);
ServerInstance->AddCommand(mycommand);
}
virtual ~ModuleSapart()
{
}
virtual Version GetVersion()
{
return Version(1,1,0,1,VF_VENDOR,API_VERSION);
}
};
MODULE_INIT(ModuleSapart)
\ No newline at end of file +/* +------------------------------------+ + * | 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 "users.h" +#include "channels.h" +#include "modules.h" + +/* $ModDesc: Provides support for unreal-style SAPART command */ + +/** Handle /SAPART + */ +class cmd_sapart : public command_t +{ + public: + cmd_sapart (InspIRCd* Instance) : command_t(Instance,"SAPART", 'o', 2) + { + this->source = "m_sapart.so"; + syntax = "<nick> <channel>"; + } + + CmdResult Handle (const char** parameters, int pcnt, userrec *user) + { + userrec* dest = ServerInstance->FindNick(parameters[0]); + chanrec* channel = ServerInstance->FindChan(parameters[1]); + if (dest && channel) + { + if (ServerInstance->ULine(dest->server)) + { + user->WriteServ("990 %s :Cannot use an SA command on a u-lined client",user->nick); + return CMD_FAILURE; + } + + /* For local clients, directly part them generating a PART message. For remote clients, + * just return CMD_SUCCESS knowing the protocol module will route the SAPART to the users + * local server and that will generate the PART instead + */ + if (IS_LOCAL(dest)) + { + if (!channel->PartUser(dest, dest->nick)) + delete channel; + chanrec* n = ServerInstance->FindChan(parameters[1]); + if (!n) + { + ServerInstance->WriteOpers("*** "+std::string(user->nick)+" used SAPART to make "+dest->nick+" part "+parameters[1]); + return CMD_SUCCESS; + } + else + { + if (!n->HasUser(dest)) + { + ServerInstance->WriteOpers("*** "+std::string(user->nick)+" used SAPART to make "+dest->nick+" part "+parameters[1]); + return CMD_SUCCESS; + } + else + { + user->WriteServ("NOTICE %s :*** Unable to make %s part %s",user->nick, dest->nick, parameters[1]); + return CMD_FAILURE; + } + } + } + else + { + ServerInstance->WriteOpers("*** "+std::string(user->nick)+" sent remote SAPART to make "+dest->nick+" part "+parameters[1]); + } + + return CMD_SUCCESS; + } + else + { + user->WriteServ("NOTICE %s :*** Invalid nickname or channel", user->nick); + } + + return CMD_FAILURE; + } +}; + + +class ModuleSapart : public Module +{ + cmd_sapart* mycommand; + public: + ModuleSapart(InspIRCd* Me) + : Module(Me) + { + + mycommand = new cmd_sapart(ServerInstance); + ServerInstance->AddCommand(mycommand); + } + + virtual ~ModuleSapart() + { + } + + virtual Version GetVersion() + { + return Version(1,1,0,1,VF_VENDOR,API_VERSION); + } + +}; + +MODULE_INIT(ModuleSapart) + diff --git a/src/modules/m_saquit.cpp b/src/modules/m_saquit.cpp index 36d511af9..d2fa8e89b 100644 --- a/src/modules/m_saquit.cpp +++ b/src/modules/m_saquit.cpp @@ -1 +1,82 @@ -/* +------------------------------------+
* | 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 "users.h"
#include "channels.h"
#include "modules.h"
/* $ModDesc: Provides support for an SAQUIT command, exits user with a reason */
/** Handle /SAQUIT
*/
class cmd_saquit : public command_t
{
public:
cmd_saquit (InspIRCd* Instance) : command_t(Instance,"SAQUIT",'o',2)
{
this->source = "m_saquit.so";
syntax = "<nick> <reason>";
}
CmdResult Handle (const char** parameters, int pcnt, userrec *user)
{
userrec* dest = ServerInstance->FindNick(parameters[0]);
if (dest)
{
if (ServerInstance->ULine(dest->server))
{
user->WriteServ("990 %s :Cannot use an SA command on a u-lined client",user->nick);
return CMD_FAILURE;
}
irc::stringjoiner reason_join(" ", parameters, 1, pcnt - 1);
std::string line = reason_join.GetJoined();
ServerInstance->WriteOpers("*** "+std::string(user->nick)+" used SAQUIT to make "+std::string(dest->nick)+" quit with a reason of "+line);
userrec::QuitUser(ServerInstance, dest, line);
return CMD_SUCCESS;
}
else
{
user->WriteServ("NOTICE %s :*** Invalid nickname '%s'", user->nick, parameters[0]);
}
return CMD_FAILURE;
}
};
class ModuleSaquit : public Module
{
cmd_saquit* mycommand;
public:
ModuleSaquit(InspIRCd* Me)
: Module(Me)
{
mycommand = new cmd_saquit(ServerInstance);
ServerInstance->AddCommand(mycommand);
}
virtual ~ModuleSaquit()
{
}
virtual Version GetVersion()
{
return Version(1,1,0,1,VF_VENDOR,API_VERSION);
}
};
MODULE_INIT(ModuleSaquit)
\ No newline at end of file +/* +------------------------------------+ + * | 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 "users.h" +#include "channels.h" +#include "modules.h" + +/* $ModDesc: Provides support for an SAQUIT command, exits user with a reason */ + +/** Handle /SAQUIT + */ +class cmd_saquit : public command_t +{ + public: + cmd_saquit (InspIRCd* Instance) : command_t(Instance,"SAQUIT",'o',2) + { + this->source = "m_saquit.so"; + syntax = "<nick> <reason>"; + } + + CmdResult Handle (const char** parameters, int pcnt, userrec *user) + { + userrec* dest = ServerInstance->FindNick(parameters[0]); + if (dest) + { + if (ServerInstance->ULine(dest->server)) + { + user->WriteServ("990 %s :Cannot use an SA command on a u-lined client",user->nick); + return CMD_FAILURE; + } + irc::stringjoiner reason_join(" ", parameters, 1, pcnt - 1); + std::string line = reason_join.GetJoined(); + + ServerInstance->WriteOpers("*** "+std::string(user->nick)+" used SAQUIT to make "+std::string(dest->nick)+" quit with a reason of "+line); + userrec::QuitUser(ServerInstance, dest, line); + + return CMD_SUCCESS; + } + else + { + user->WriteServ("NOTICE %s :*** Invalid nickname '%s'", user->nick, parameters[0]); + } + + return CMD_FAILURE; + } +}; + +class ModuleSaquit : public Module +{ + cmd_saquit* mycommand; + public: + ModuleSaquit(InspIRCd* Me) + : Module(Me) + { + + mycommand = new cmd_saquit(ServerInstance); + ServerInstance->AddCommand(mycommand); + } + + virtual ~ModuleSaquit() + { + } + + virtual Version GetVersion() + { + return Version(1,1,0,1,VF_VENDOR,API_VERSION); + } + +}; + +MODULE_INIT(ModuleSaquit) diff --git a/src/modules/m_securelist.cpp b/src/modules/m_securelist.cpp index 264090311..8761716c0 100644 --- a/src/modules/m_securelist.cpp +++ b/src/modules/m_securelist.cpp @@ -1 +1,97 @@ -/* +------------------------------------+
* | 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 "users.h"
#include "channels.h"
#include "modules.h"
/* $ModDesc: A module overriding /list, and making it safe - stop those sendq problems. */
class ModuleSecureList : public Module
{
private:
std::vector<std::string> allowlist;
time_t WaitTime;
public:
ModuleSecureList(InspIRCd* Me) : Module(Me)
{
OnRehash(NULL,"");
}
virtual ~ModuleSecureList()
{
}
virtual Version GetVersion()
{
return Version(1,1,0,0,VF_VENDOR,API_VERSION);
}
void OnRehash(userrec* user, const std::string ¶meter)
{
ConfigReader* MyConf = new ConfigReader(ServerInstance);
allowlist.clear();
for (int i = 0; i < MyConf->Enumerate("securehost"); i++)
allowlist.push_back(MyConf->ReadValue("securehost", "exception", i));
WaitTime = MyConf->ReadInteger("securelist", "waittime", "60", 0, true);
DELETE(MyConf);
}
void Implements(char* List)
{
List[I_OnRehash] = List[I_OnPreCommand] = List[I_On005Numeric] = 1;
}
/*
* OnPreCommand()
* Intercept the LIST command.
*/
virtual int OnPreCommand(const std::string &command, const char** parameters, int pcnt, userrec *user, bool validated, const std::string &original_line)
{
/* If the command doesnt appear to be valid, we dont want to mess with it. */
if (!validated)
return 0;
if ((command == "LIST") && (ServerInstance->Time() < (user->signon+WaitTime)) && (!IS_OPER(user)))
{
/* Normally wouldnt be allowed here, are they exempt? */
for (std::vector<std::string>::iterator x = allowlist.begin(); x != allowlist.end(); x++)
if (ServerInstance->MatchText(user->MakeHost(), *x))
return 0;
/* Not exempt, BOOK EM DANNO! */
user->WriteServ("NOTICE %s :*** You cannot list within the first %d seconds of connecting. Please try again later.",user->nick, WaitTime);
/* Some crap clients (read: mIRC, various java chat applets) muck up if they don't
* receive these numerics whenever they send LIST, so give them an empty LIST to mull over.
*/
user->WriteServ("321 %s Channel :Users Name",user->nick);
user->WriteServ("323 %s :End of channel list.",user->nick);
return 1;
}
return 0;
}
virtual void On005Numeric(std::string &output)
{
output.append(" SECURELIST");
}
virtual Priority Prioritize()
{
return (Priority)ServerInstance->PriorityBefore("m_safelist.so");
}
};
MODULE_INIT(ModuleSecureList)
\ No newline at end of file +/* +------------------------------------+ + * | 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 "users.h" +#include "channels.h" +#include "modules.h" + +/* $ModDesc: A module overriding /list, and making it safe - stop those sendq problems. */ + +class ModuleSecureList : public Module +{ + private: + std::vector<std::string> allowlist; + time_t WaitTime; + public: + ModuleSecureList(InspIRCd* Me) : Module(Me) + { + OnRehash(NULL,""); + } + + virtual ~ModuleSecureList() + { + } + + virtual Version GetVersion() + { + return Version(1,1,0,0,VF_VENDOR,API_VERSION); + } + + void OnRehash(userrec* user, const std::string ¶meter) + { + ConfigReader* MyConf = new ConfigReader(ServerInstance); + allowlist.clear(); + for (int i = 0; i < MyConf->Enumerate("securehost"); i++) + allowlist.push_back(MyConf->ReadValue("securehost", "exception", i)); + WaitTime = MyConf->ReadInteger("securelist", "waittime", "60", 0, true); + DELETE(MyConf); + } + + void Implements(char* List) + { + List[I_OnRehash] = List[I_OnPreCommand] = List[I_On005Numeric] = 1; + } + + /* + * OnPreCommand() + * Intercept the LIST command. + */ + virtual int OnPreCommand(const std::string &command, const char** parameters, int pcnt, userrec *user, bool validated, const std::string &original_line) + { + /* If the command doesnt appear to be valid, we dont want to mess with it. */ + if (!validated) + return 0; + + if ((command == "LIST") && (ServerInstance->Time() < (user->signon+WaitTime)) && (!IS_OPER(user))) + { + /* Normally wouldnt be allowed here, are they exempt? */ + for (std::vector<std::string>::iterator x = allowlist.begin(); x != allowlist.end(); x++) + if (ServerInstance->MatchText(user->MakeHost(), *x)) + return 0; + + /* Not exempt, BOOK EM DANNO! */ + user->WriteServ("NOTICE %s :*** You cannot list within the first %d seconds of connecting. Please try again later.",user->nick, WaitTime); + /* Some crap clients (read: mIRC, various java chat applets) muck up if they don't + * receive these numerics whenever they send LIST, so give them an empty LIST to mull over. + */ + user->WriteServ("321 %s Channel :Users Name",user->nick); + user->WriteServ("323 %s :End of channel list.",user->nick); + return 1; + } + return 0; + } + + virtual void On005Numeric(std::string &output) + { + output.append(" SECURELIST"); + } + + virtual Priority Prioritize() + { + return (Priority)ServerInstance->PriorityBefore("m_safelist.so"); + } + +}; + +MODULE_INIT(ModuleSecureList) diff --git a/src/modules/m_seenicks.cpp b/src/modules/m_seenicks.cpp index 2e7755810..215cd34b0 100644 --- a/src/modules/m_seenicks.cpp +++ b/src/modules/m_seenicks.cpp @@ -1 +1,55 @@ -/* +------------------------------------+
* | 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 "users.h"
#include "channels.h"
#include "modules.h"
#include "hashcomp.h"
#include "configreader.h"
/* $ModDesc: Provides support for seeing local and remote nickchanges via snomasks */
class ModuleSeeNicks : public Module
{
public:
ModuleSeeNicks(InspIRCd* Me)
: Module(Me)
{
ServerInstance->SNO->EnableSnomask('n',"NICK");
ServerInstance->SNO->EnableSnomask('N',"REMOTENICK");
}
virtual ~ModuleSeeNicks()
{
ServerInstance->SNO->DisableSnomask('n');
ServerInstance->SNO->DisableSnomask('N');
}
virtual Version GetVersion()
{
return Version(1,1,0,1, VF_VENDOR, API_VERSION);
}
void Implements(char* List)
{
List[I_OnUserPostNick] = 1;
}
virtual void OnUserPostNick(userrec* user, const std::string &oldnick)
{
ServerInstance->SNO->WriteToSnoMask(IS_LOCAL(user) ? 'n' : 'N',"User %s changed their nickname to %s", oldnick.c_str(), user->nick);
}
};
MODULE_INIT(ModuleSeeNicks)
\ No newline at end of file +/* +------------------------------------+ + * | 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 "users.h" +#include "channels.h" +#include "modules.h" +#include "hashcomp.h" +#include "configreader.h" + +/* $ModDesc: Provides support for seeing local and remote nickchanges via snomasks */ + +class ModuleSeeNicks : public Module +{ + public: + ModuleSeeNicks(InspIRCd* Me) + : Module(Me) + { + ServerInstance->SNO->EnableSnomask('n',"NICK"); + ServerInstance->SNO->EnableSnomask('N',"REMOTENICK"); + } + + virtual ~ModuleSeeNicks() + { + ServerInstance->SNO->DisableSnomask('n'); + ServerInstance->SNO->DisableSnomask('N'); + } + + virtual Version GetVersion() + { + return Version(1,1,0,1, VF_VENDOR, API_VERSION); + } + + void Implements(char* List) + { + List[I_OnUserPostNick] = 1; + } + + virtual void OnUserPostNick(userrec* user, const std::string &oldnick) + { + ServerInstance->SNO->WriteToSnoMask(IS_LOCAL(user) ? 'n' : 'N',"User %s changed their nickname to %s", oldnick.c_str(), user->nick); + } +}; + +MODULE_INIT(ModuleSeeNicks) diff --git a/src/modules/m_services.cpp b/src/modules/m_services.cpp index d429b2860..22b5dfcb5 100644 --- a/src/modules/m_services.cpp +++ b/src/modules/m_services.cpp @@ -1 +1,310 @@ -/* +------------------------------------+
* | 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 "users.h"
#include "channels.h"
#include "modules.h"
static bool kludgeme = false;
/* $ModDesc: Povides support for services +r user/chan modes and more */
/** Channel mode +r - mark a channel as identified
*/
class Channel_r : public ModeHandler
{
public:
Channel_r(InspIRCd* Instance) : ModeHandler(Instance, 'r', 0, 0, false, MODETYPE_CHANNEL, false) { }
ModeAction OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string ¶meter, bool adding)
{
// only a u-lined server may add or remove the +r mode.
if ((ServerInstance->ULine(source->nick)) || (ServerInstance->ULine(source->server)) || (!*source->server || (strchr(source->nick,'.'))))
{
channel->SetMode('r',adding);
return MODEACTION_ALLOW;
}
else
{
source->WriteServ("500 %s :Only a server may modify the +r channel mode", source->nick);
return MODEACTION_DENY;
}
}
};
/** User mode +r - mark a user as identified
*/
class User_r : public ModeHandler
{
public:
User_r(InspIRCd* Instance) : ModeHandler(Instance, 'r', 0, 0, false, MODETYPE_USER, false) { }
ModeAction OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string ¶meter, bool adding)
{
if ((kludgeme) || (ServerInstance->ULine(source->nick)) || (ServerInstance->ULine(source->server)) || (!*source->server || (strchr(source->nick,'.'))))
{
if ((adding && !dest->IsModeSet('r')) || (!adding && dest->IsModeSet('r')))
{
dest->SetMode('r',adding);
return MODEACTION_ALLOW;
}
return MODEACTION_DENY;
}
else
{
source->WriteServ("500 %s :Only a server may modify the +r user mode", source->nick);
return MODEACTION_DENY;
}
}
};
/** Channel mode +R - registered users only
*/
class Channel_R : public ModeHandler
{
public:
Channel_R(InspIRCd* Instance) : ModeHandler(Instance, 'R', 0, 0, false, MODETYPE_CHANNEL, false) { }
ModeAction OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string ¶meter, bool adding)
{
if (adding)
{
if (!channel->IsModeSet('R'))
{
channel->SetMode('R',true);
return MODEACTION_ALLOW;
}
}
else
{
if (channel->IsModeSet('R'))
{
channel->SetMode('R',false);
return MODEACTION_ALLOW;
}
}
return MODEACTION_DENY;
}
};
/** User mode +R - only allow PRIVMSG and NOTICE from registered users
*/
class User_R : public ModeHandler
{
public:
User_R(InspIRCd* Instance) : ModeHandler(Instance, 'R', 0, 0, false, MODETYPE_USER, false) { }
ModeAction OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string ¶meter, bool adding)
{
if (adding)
{
if (!dest->IsModeSet('R'))
{
dest->SetMode('R',true);
return MODEACTION_ALLOW;
}
}
else
{
if (dest->IsModeSet('R'))
{
dest->SetMode('R',false);
return MODEACTION_ALLOW;
}
}
return MODEACTION_DENY;
}
};
/** Channel mode +M - only allow privmsg and notice to channel from registered users
*/
class Channel_M : public ModeHandler
{
public:
Channel_M(InspIRCd* Instance) : ModeHandler(Instance, 'M', 0, 0, false, MODETYPE_CHANNEL, false) { }
ModeAction OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string ¶meter, bool adding)
{
if (adding)
{
if (!channel->IsModeSet('M'))
{
channel->SetMode('M',true);
return MODEACTION_ALLOW;
}
}
else
{
if (channel->IsModeSet('M'))
{
channel->SetMode('M',false);
return MODEACTION_ALLOW;
}
}
return MODEACTION_DENY;
}
};
/** Dreamnforge-like services support
*/
class ModuleServices : public Module
{
Channel_r* m1;
Channel_R* m2;
Channel_M* m3;
User_r* m4;
User_R* m5;
public:
ModuleServices(InspIRCd* Me)
: Module(Me)
{
m1 = new Channel_r(ServerInstance);
m2 = new Channel_R(ServerInstance);
m3 = new Channel_M(ServerInstance);
m4 = new User_r(ServerInstance);
m5 = new User_R(ServerInstance);
if (!ServerInstance->AddMode(m1, 'r') || !ServerInstance->AddMode(m2, 'R') || !ServerInstance->AddMode(m3, 'M')
|| !ServerInstance->AddMode(m4, 'r') || !ServerInstance->AddMode(m5, 'R'))
{
throw ModuleException("Could not add user and channel modes!");
}
kludgeme = false;
}
/* <- :stitch.chatspike.net 307 w00t w00t :is a registered nick */
virtual void OnWhois(userrec* source, userrec* dest)
{
if (dest->IsModeSet('r'))
{
/* user is registered */
ServerInstance->SendWhoisLine(source, dest, 307, "%s %s :is a registered nick", source->nick, dest->nick);
}
}
void Implements(char* List)
{
List[I_OnWhois] = List[I_OnUserPostNick] = List[I_OnUserPreMessage] = List[I_OnUserPreNotice] = List[I_OnUserPreJoin] = 1;
}
virtual void OnUserPostNick(userrec* user, const std::string &oldnick)
{
/* On nickchange, if they have +r, remove it */
if (user->IsModeSet('r'))
{
const char* modechange[2];
modechange[0] = user->nick;
modechange[1] = "-r";
kludgeme = true;
ServerInstance->SendMode(modechange,2,user);
kludgeme = false;
}
}
virtual int OnUserPreMessage(userrec* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list)
{
if (!IS_LOCAL(user))
return 0;
if (target_type == TYPE_CHANNEL)
{
chanrec* c = (chanrec*)dest;
if ((c->IsModeSet('M')) && (!user->IsModeSet('r')))
{
if ((ServerInstance->ULine(user->nick)) || (ServerInstance->ULine(user->server)))
{
// user is ulined, can speak regardless
return 0;
}
// user messaging a +M channel and is not registered
user->WriteServ("477 %s %s :You need a registered nickname to speak on this channel", user->nick, c->name);
return 1;
}
}
if (target_type == TYPE_USER)
{
userrec* u = (userrec*)dest;
if ((u->IsModeSet('R')) && (!user->IsModeSet('r')))
{
if ((ServerInstance->ULine(user->nick)) || (ServerInstance->ULine(user->server)))
{
// user is ulined, can speak regardless
return 0;
}
// user messaging a +R user and is not registered
user->WriteServ("477 %s %s :You need a registered nickname to message this user", user->nick, u->nick);
return 1;
}
}
return 0;
}
virtual int OnUserPreNotice(userrec* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list)
{
return OnUserPreMessage(user,dest,target_type,text,status, exempt_list);
}
virtual int OnUserPreJoin(userrec* user, chanrec* chan, const char* cname, std::string &privs)
{
if (chan)
{
if (chan->IsModeSet('R'))
{
if (!user->IsModeSet('r'))
{
if ((ServerInstance->ULine(user->nick)) || (ServerInstance->ULine(user->server)))
{
// user is ulined, won't be stopped from joining
return 0;
}
// joining a +R channel and not identified
user->WriteServ("477 %s %s :You need a registered nickname to join this channel", user->nick, chan->name);
return 1;
}
}
}
return 0;
}
virtual ~ModuleServices()
{
kludgeme = true;
ServerInstance->Modes->DelMode(m1);
ServerInstance->Modes->DelMode(m2);
ServerInstance->Modes->DelMode(m3);
ServerInstance->Modes->DelMode(m4);
ServerInstance->Modes->DelMode(m5);
DELETE(m1);
DELETE(m2);
DELETE(m3);
DELETE(m4);
DELETE(m5);
}
virtual Version GetVersion()
{
return Version(1,1,0,0,VF_COMMON|VF_VENDOR,API_VERSION);
}
};
MODULE_INIT(ModuleServices)
\ No newline at end of file +/* +------------------------------------+ + * | 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 "users.h" +#include "channels.h" +#include "modules.h" + +static bool kludgeme = false; + +/* $ModDesc: Povides support for services +r user/chan modes and more */ + +/** Channel mode +r - mark a channel as identified + */ +class Channel_r : public ModeHandler +{ + + public: + Channel_r(InspIRCd* Instance) : ModeHandler(Instance, 'r', 0, 0, false, MODETYPE_CHANNEL, false) { } + + ModeAction OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string ¶meter, bool adding) + { + // only a u-lined server may add or remove the +r mode. + if ((ServerInstance->ULine(source->nick)) || (ServerInstance->ULine(source->server)) || (!*source->server || (strchr(source->nick,'.')))) + { + channel->SetMode('r',adding); + return MODEACTION_ALLOW; + } + else + { + source->WriteServ("500 %s :Only a server may modify the +r channel mode", source->nick); + return MODEACTION_DENY; + } + } +}; + +/** User mode +r - mark a user as identified + */ +class User_r : public ModeHandler +{ + + public: + User_r(InspIRCd* Instance) : ModeHandler(Instance, 'r', 0, 0, false, MODETYPE_USER, false) { } + + ModeAction OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string ¶meter, bool adding) + { + if ((kludgeme) || (ServerInstance->ULine(source->nick)) || (ServerInstance->ULine(source->server)) || (!*source->server || (strchr(source->nick,'.')))) + { + if ((adding && !dest->IsModeSet('r')) || (!adding && dest->IsModeSet('r'))) + { + dest->SetMode('r',adding); + return MODEACTION_ALLOW; + } + return MODEACTION_DENY; + } + else + { + source->WriteServ("500 %s :Only a server may modify the +r user mode", source->nick); + return MODEACTION_DENY; + } + } +}; + +/** Channel mode +R - registered users only + */ +class Channel_R : public ModeHandler +{ + public: + Channel_R(InspIRCd* Instance) : ModeHandler(Instance, 'R', 0, 0, false, MODETYPE_CHANNEL, false) { } + + ModeAction OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string ¶meter, bool adding) + { + if (adding) + { + if (!channel->IsModeSet('R')) + { + channel->SetMode('R',true); + return MODEACTION_ALLOW; + } + } + else + { + if (channel->IsModeSet('R')) + { + channel->SetMode('R',false); + return MODEACTION_ALLOW; + } + } + + return MODEACTION_DENY; + } +}; + +/** User mode +R - only allow PRIVMSG and NOTICE from registered users + */ +class User_R : public ModeHandler +{ + public: + User_R(InspIRCd* Instance) : ModeHandler(Instance, 'R', 0, 0, false, MODETYPE_USER, false) { } + + ModeAction OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string ¶meter, bool adding) + { + if (adding) + { + if (!dest->IsModeSet('R')) + { + dest->SetMode('R',true); + return MODEACTION_ALLOW; + } + } + else + { + if (dest->IsModeSet('R')) + { + dest->SetMode('R',false); + return MODEACTION_ALLOW; + } + } + + return MODEACTION_DENY; + } +}; + +/** Channel mode +M - only allow privmsg and notice to channel from registered users + */ +class Channel_M : public ModeHandler +{ + public: + Channel_M(InspIRCd* Instance) : ModeHandler(Instance, 'M', 0, 0, false, MODETYPE_CHANNEL, false) { } + + ModeAction OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string ¶meter, bool adding) + { + if (adding) + { + if (!channel->IsModeSet('M')) + { + channel->SetMode('M',true); + return MODEACTION_ALLOW; + } + } + else + { + if (channel->IsModeSet('M')) + { + channel->SetMode('M',false); + return MODEACTION_ALLOW; + } + } + + return MODEACTION_DENY; + } +}; + +/** Dreamnforge-like services support + */ +class ModuleServices : public Module +{ + + Channel_r* m1; + Channel_R* m2; + Channel_M* m3; + User_r* m4; + User_R* m5; + public: + ModuleServices(InspIRCd* Me) + : Module(Me) + { + + m1 = new Channel_r(ServerInstance); + m2 = new Channel_R(ServerInstance); + m3 = new Channel_M(ServerInstance); + m4 = new User_r(ServerInstance); + m5 = new User_R(ServerInstance); + + if (!ServerInstance->AddMode(m1, 'r') || !ServerInstance->AddMode(m2, 'R') || !ServerInstance->AddMode(m3, 'M') + || !ServerInstance->AddMode(m4, 'r') || !ServerInstance->AddMode(m5, 'R')) + { + throw ModuleException("Could not add user and channel modes!"); + } + + kludgeme = false; + } + + /* <- :stitch.chatspike.net 307 w00t w00t :is a registered nick */ + virtual void OnWhois(userrec* source, userrec* dest) + { + if (dest->IsModeSet('r')) + { + /* user is registered */ + ServerInstance->SendWhoisLine(source, dest, 307, "%s %s :is a registered nick", source->nick, dest->nick); + } + } + + void Implements(char* List) + { + List[I_OnWhois] = List[I_OnUserPostNick] = List[I_OnUserPreMessage] = List[I_OnUserPreNotice] = List[I_OnUserPreJoin] = 1; + } + + virtual void OnUserPostNick(userrec* user, const std::string &oldnick) + { + /* On nickchange, if they have +r, remove it */ + if (user->IsModeSet('r')) + { + const char* modechange[2]; + modechange[0] = user->nick; + modechange[1] = "-r"; + kludgeme = true; + ServerInstance->SendMode(modechange,2,user); + kludgeme = false; + } + } + + virtual int OnUserPreMessage(userrec* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list) + { + if (!IS_LOCAL(user)) + return 0; + + if (target_type == TYPE_CHANNEL) + { + chanrec* c = (chanrec*)dest; + if ((c->IsModeSet('M')) && (!user->IsModeSet('r'))) + { + if ((ServerInstance->ULine(user->nick)) || (ServerInstance->ULine(user->server))) + { + // user is ulined, can speak regardless + return 0; + } + // user messaging a +M channel and is not registered + user->WriteServ("477 %s %s :You need a registered nickname to speak on this channel", user->nick, c->name); + return 1; + } + } + if (target_type == TYPE_USER) + { + userrec* u = (userrec*)dest; + if ((u->IsModeSet('R')) && (!user->IsModeSet('r'))) + { + if ((ServerInstance->ULine(user->nick)) || (ServerInstance->ULine(user->server))) + { + // user is ulined, can speak regardless + return 0; + } + // user messaging a +R user and is not registered + user->WriteServ("477 %s %s :You need a registered nickname to message this user", user->nick, u->nick); + return 1; + } + } + return 0; + } + + virtual int OnUserPreNotice(userrec* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list) + { + return OnUserPreMessage(user,dest,target_type,text,status, exempt_list); + } + + virtual int OnUserPreJoin(userrec* user, chanrec* chan, const char* cname, std::string &privs) + { + if (chan) + { + if (chan->IsModeSet('R')) + { + if (!user->IsModeSet('r')) + { + if ((ServerInstance->ULine(user->nick)) || (ServerInstance->ULine(user->server))) + { + // user is ulined, won't be stopped from joining + return 0; + } + // joining a +R channel and not identified + user->WriteServ("477 %s %s :You need a registered nickname to join this channel", user->nick, chan->name); + return 1; + } + } + } + return 0; + } + + virtual ~ModuleServices() + { + kludgeme = true; + ServerInstance->Modes->DelMode(m1); + ServerInstance->Modes->DelMode(m2); + ServerInstance->Modes->DelMode(m3); + ServerInstance->Modes->DelMode(m4); + ServerInstance->Modes->DelMode(m5); + DELETE(m1); + DELETE(m2); + DELETE(m3); + DELETE(m4); + DELETE(m5); + } + + virtual Version GetVersion() + { + return Version(1,1,0,0,VF_COMMON|VF_VENDOR,API_VERSION); + } +}; + + +MODULE_INIT(ModuleServices) diff --git a/src/modules/m_services_account.cpp b/src/modules/m_services_account.cpp index 3d32e3156..cff0d7698 100644 --- a/src/modules/m_services_account.cpp +++ b/src/modules/m_services_account.cpp @@ -1 +1,332 @@ -/* +------------------------------------+
* | 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 "users.h"
#include "channels.h"
#include "modules.h"
#include "hashcomp.h"
/* $ModDesc: Povides support for ircu-style services accounts, including chmode +R, etc. */
/** Channel mode +R - unidentified users cannot join
*/
class AChannel_R : public ModeHandler
{
public:
AChannel_R(InspIRCd* Instance) : ModeHandler(Instance, 'R', 0, 0, false, MODETYPE_CHANNEL, false) { }
ModeAction OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string ¶meter, bool adding)
{
if (adding)
{
if (!channel->IsModeSet('R'))
{
channel->SetMode('R',true);
return MODEACTION_ALLOW;
}
}
else
{
if (channel->IsModeSet('R'))
{
channel->SetMode('R',false);
return MODEACTION_ALLOW;
}
}
return MODEACTION_DENY;
}
};
/** User mode +R - unidentified users cannot message
*/
class AUser_R : public ModeHandler
{
public:
AUser_R(InspIRCd* Instance) : ModeHandler(Instance, 'R', 0, 0, false, MODETYPE_USER, false) { }
ModeAction OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string ¶meter, bool adding)
{
if (adding)
{
if (!dest->IsModeSet('R'))
{
dest->SetMode('R',true);
return MODEACTION_ALLOW;
}
}
else
{
if (dest->IsModeSet('R'))
{
dest->SetMode('R',false);
return MODEACTION_ALLOW;
}
}
return MODEACTION_DENY;
}
};
/** Channel mode +M - unidentified users cannot message channel
*/
class AChannel_M : public ModeHandler
{
public:
AChannel_M(InspIRCd* Instance) : ModeHandler(Instance, 'M', 0, 0, false, MODETYPE_CHANNEL, false) { }
ModeAction OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string ¶meter, bool adding)
{
if (adding)
{
if (!channel->IsModeSet('M'))
{
channel->SetMode('M',true);
return MODEACTION_ALLOW;
}
}
else
{
if (channel->IsModeSet('M'))
{
channel->SetMode('M',false);
return MODEACTION_ALLOW;
}
}
return MODEACTION_DENY;
}
};
class ModuleServicesAccount : public Module
{
AChannel_R* m1;
AChannel_M* m2;
AUser_R* m3;
public:
ModuleServicesAccount(InspIRCd* Me) : Module(Me)
{
m1 = new AChannel_R(ServerInstance);
m2 = new AChannel_M(ServerInstance);
m3 = new AUser_R(ServerInstance);
if (!ServerInstance->AddMode(m1, 'R') || !ServerInstance->AddMode(m2, 'M') || !ServerInstance->AddMode(m3, 'R'))
throw ModuleException("Could not add new modes!");
}
/* <- :twisted.oscnet.org 330 w00t2 w00t2 w00t :is logged in as */
virtual void OnWhois(userrec* source, userrec* dest)
{
std::string *account;
dest->GetExt("accountname", account);
if (account)
{
ServerInstance->SendWhoisLine(source, dest, 330, "%s %s %s :is logged in as", source->nick, dest->nick, account->c_str());
}
}
void Implements(char* List)
{
List[I_OnWhois] = List[I_OnUserPreMessage] = List[I_OnUserPreNotice] = List[I_OnUserPreJoin] = 1;
List[I_OnSyncUserMetaData] = List[I_OnUserQuit] = List[I_OnCleanup] = List[I_OnDecodeMetaData] = 1;
}
virtual int OnUserPreMessage(userrec* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list)
{
std::string *account;
if (!IS_LOCAL(user))
return 0;
user->GetExt("accountname", account);
if (target_type == TYPE_CHANNEL)
{
chanrec* c = (chanrec*)dest;
if ((c->IsModeSet('M')) && (!account))
{
if ((ServerInstance->ULine(user->nick)) || (ServerInstance->ULine(user->server)))
{
// user is ulined, can speak regardless
return 0;
}
// user messaging a +M channel and is not registered
user->WriteServ("477 "+std::string(user->nick)+" "+std::string(c->name)+" :You need to be identified to a registered account to message this channel");
return 1;
}
}
if (target_type == TYPE_USER)
{
userrec* u = (userrec*)dest;
if ((u->modes['R'-65]) && (!account))
{
if ((ServerInstance->ULine(user->nick)) || (ServerInstance->ULine(user->server)))
{
// user is ulined, can speak regardless
return 0;
}
// user messaging a +R user and is not registered
user->WriteServ("477 "+std::string(user->nick)+" "+std::string(u->nick)+" :You need to be identified to a registered account to message this user");
return 1;
}
}
return 0;
}
virtual int OnUserPreNotice(userrec* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list)
{
return OnUserPreMessage(user, dest, target_type, text, status, exempt_list);
}
virtual int OnUserPreJoin(userrec* user, chanrec* chan, const char* cname, std::string &privs)
{
std::string *account;
user->GetExt("accountname", account);
if (chan)
{
if (chan->IsModeSet('R'))
{
if (!account)
{
if ((ServerInstance->ULine(user->nick)) || (ServerInstance->ULine(user->server)))
{
// user is ulined, won't be stopped from joining
return 0;
}
// joining a +R channel and not identified
user->WriteServ("477 "+std::string(user->nick)+" "+std::string(chan->name)+" :You need to be identified to a registered account to join this channel");
return 1;
}
}
}
return 0;
}
// Whenever the linking module wants to send out data, but doesnt know what the data
// represents (e.g. it is metadata, added to a userrec or chanrec by a module) then
// this method is called. We should use the ProtoSendMetaData function after we've
// corrected decided how the data should look, to send the metadata on its way if
// it is ours.
virtual void OnSyncUserMetaData(userrec* user, Module* proto, void* opaque, const std::string &extname, bool displayable)
{
// check if the linking module wants to know about OUR metadata
if (extname == "accountname")
{
// check if this user has an swhois field to send
std::string* account;
user->GetExt("accountname", account);
if (account)
{
// remove any accidental leading/trailing spaces
trim(*account);
// call this function in the linking module, let it format the data how it
// sees fit, and send it on its way. We dont need or want to know how.
proto->ProtoSendMetaData(opaque,TYPE_USER,user,extname,*account);
}
}
}
// when a user quits, tidy up their metadata
virtual void OnUserQuit(userrec* user, const std::string &message, const std::string &oper_message)
{
std::string* account;
user->GetExt("accountname", account);
if (account)
{
user->Shrink("accountname");
delete account;
}
}
// if the module is unloaded, tidy up all our dangling metadata
virtual void OnCleanup(int target_type, void* item)
{
if (target_type == TYPE_USER)
{
userrec* user = (userrec*)item;
std::string* account;
user->GetExt("accountname", account);
if (account)
{
user->Shrink("accountname");
delete account;
}
}
}
// Whenever the linking module receives metadata from another server and doesnt know what
// to do with it (of course, hence the 'meta') it calls this method, and it is up to each
// module in turn to figure out if this metadata key belongs to them, and what they want
// to do with it.
// In our case we're only sending a single string around, so we just construct a std::string.
// Some modules will probably get much more complex and format more detailed structs and classes
// in a textual way for sending over the link.
virtual void OnDecodeMetaData(int target_type, void* target, const std::string &extname, const std::string &extdata)
{
// check if its our metadata key, and its associated with a user
if ((target_type == TYPE_USER) && (extname == "accountname"))
{
userrec* dest = (userrec*)target;
/* logging them out? */
if (extdata.empty())
{
std::string* account;
dest->GetExt("accountname", account);
if (account)
{
dest->Shrink("accountname");
delete account;
}
}
else
{
// if they dont already have an accountname field, accept the remote server's
std::string* text;
if (!dest->GetExt("accountname", text))
{
text = new std::string(extdata);
// remove any accidental leading/trailing spaces
trim(*text);
dest->Extend("accountname", text);
}
}
}
}
virtual ~ModuleServicesAccount()
{
ServerInstance->Modes->DelMode(m1);
ServerInstance->Modes->DelMode(m2);
ServerInstance->Modes->DelMode(m3);
DELETE(m1);
DELETE(m2);
DELETE(m3);
}
virtual Version GetVersion()
{
return Version(1,1,0,0,VF_COMMON|VF_VENDOR,API_VERSION);
}
};
MODULE_INIT(ModuleServicesAccount)
\ No newline at end of file +/* +------------------------------------+ + * | 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 "users.h" +#include "channels.h" +#include "modules.h" +#include "hashcomp.h" + +/* $ModDesc: Povides support for ircu-style services accounts, including chmode +R, etc. */ + +/** Channel mode +R - unidentified users cannot join + */ +class AChannel_R : public ModeHandler +{ + public: + AChannel_R(InspIRCd* Instance) : ModeHandler(Instance, 'R', 0, 0, false, MODETYPE_CHANNEL, false) { } + + ModeAction OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string ¶meter, bool adding) + { + if (adding) + { + if (!channel->IsModeSet('R')) + { + channel->SetMode('R',true); + return MODEACTION_ALLOW; + } + } + else + { + if (channel->IsModeSet('R')) + { + channel->SetMode('R',false); + return MODEACTION_ALLOW; + } + } + + return MODEACTION_DENY; + } +}; + +/** User mode +R - unidentified users cannot message + */ +class AUser_R : public ModeHandler +{ + public: + AUser_R(InspIRCd* Instance) : ModeHandler(Instance, 'R', 0, 0, false, MODETYPE_USER, false) { } + + ModeAction OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string ¶meter, bool adding) + { + if (adding) + { + if (!dest->IsModeSet('R')) + { + dest->SetMode('R',true); + return MODEACTION_ALLOW; + } + } + else + { + if (dest->IsModeSet('R')) + { + dest->SetMode('R',false); + return MODEACTION_ALLOW; + } + } + + return MODEACTION_DENY; + } +}; + +/** Channel mode +M - unidentified users cannot message channel + */ +class AChannel_M : public ModeHandler +{ + public: + AChannel_M(InspIRCd* Instance) : ModeHandler(Instance, 'M', 0, 0, false, MODETYPE_CHANNEL, false) { } + + ModeAction OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string ¶meter, bool adding) + { + if (adding) + { + if (!channel->IsModeSet('M')) + { + channel->SetMode('M',true); + return MODEACTION_ALLOW; + } + } + else + { + if (channel->IsModeSet('M')) + { + channel->SetMode('M',false); + return MODEACTION_ALLOW; + } + } + + return MODEACTION_DENY; + } +}; + +class ModuleServicesAccount : public Module +{ + + AChannel_R* m1; + AChannel_M* m2; + AUser_R* m3; + public: + ModuleServicesAccount(InspIRCd* Me) : Module(Me) + { + + m1 = new AChannel_R(ServerInstance); + m2 = new AChannel_M(ServerInstance); + m3 = new AUser_R(ServerInstance); + if (!ServerInstance->AddMode(m1, 'R') || !ServerInstance->AddMode(m2, 'M') || !ServerInstance->AddMode(m3, 'R')) + throw ModuleException("Could not add new modes!"); + } + + /* <- :twisted.oscnet.org 330 w00t2 w00t2 w00t :is logged in as */ + virtual void OnWhois(userrec* source, userrec* dest) + { + std::string *account; + dest->GetExt("accountname", account); + + if (account) + { + ServerInstance->SendWhoisLine(source, dest, 330, "%s %s %s :is logged in as", source->nick, dest->nick, account->c_str()); + } + } + + void Implements(char* List) + { + List[I_OnWhois] = List[I_OnUserPreMessage] = List[I_OnUserPreNotice] = List[I_OnUserPreJoin] = 1; + List[I_OnSyncUserMetaData] = List[I_OnUserQuit] = List[I_OnCleanup] = List[I_OnDecodeMetaData] = 1; + } + + virtual int OnUserPreMessage(userrec* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list) + { + std::string *account; + + if (!IS_LOCAL(user)) + return 0; + + user->GetExt("accountname", account); + + if (target_type == TYPE_CHANNEL) + { + chanrec* c = (chanrec*)dest; + + if ((c->IsModeSet('M')) && (!account)) + { + if ((ServerInstance->ULine(user->nick)) || (ServerInstance->ULine(user->server))) + { + // user is ulined, can speak regardless + return 0; + } + + // user messaging a +M channel and is not registered + user->WriteServ("477 "+std::string(user->nick)+" "+std::string(c->name)+" :You need to be identified to a registered account to message this channel"); + return 1; + } + } + if (target_type == TYPE_USER) + { + userrec* u = (userrec*)dest; + + if ((u->modes['R'-65]) && (!account)) + { + if ((ServerInstance->ULine(user->nick)) || (ServerInstance->ULine(user->server))) + { + // user is ulined, can speak regardless + return 0; + } + + // user messaging a +R user and is not registered + user->WriteServ("477 "+std::string(user->nick)+" "+std::string(u->nick)+" :You need to be identified to a registered account to message this user"); + return 1; + } + } + return 0; + } + + virtual int OnUserPreNotice(userrec* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list) + { + return OnUserPreMessage(user, dest, target_type, text, status, exempt_list); + } + + virtual int OnUserPreJoin(userrec* user, chanrec* chan, const char* cname, std::string &privs) + { + std::string *account; + user->GetExt("accountname", account); + + if (chan) + { + if (chan->IsModeSet('R')) + { + if (!account) + { + if ((ServerInstance->ULine(user->nick)) || (ServerInstance->ULine(user->server))) + { + // user is ulined, won't be stopped from joining + return 0; + } + // joining a +R channel and not identified + user->WriteServ("477 "+std::string(user->nick)+" "+std::string(chan->name)+" :You need to be identified to a registered account to join this channel"); + return 1; + } + } + } + return 0; + } + + // Whenever the linking module wants to send out data, but doesnt know what the data + // represents (e.g. it is metadata, added to a userrec or chanrec by a module) then + // this method is called. We should use the ProtoSendMetaData function after we've + // corrected decided how the data should look, to send the metadata on its way if + // it is ours. + virtual void OnSyncUserMetaData(userrec* user, Module* proto, void* opaque, const std::string &extname, bool displayable) + { + // check if the linking module wants to know about OUR metadata + if (extname == "accountname") + { + // check if this user has an swhois field to send + std::string* account; + user->GetExt("accountname", account); + if (account) + { + // remove any accidental leading/trailing spaces + trim(*account); + + // call this function in the linking module, let it format the data how it + // sees fit, and send it on its way. We dont need or want to know how. + proto->ProtoSendMetaData(opaque,TYPE_USER,user,extname,*account); + } + } + } + + // when a user quits, tidy up their metadata + virtual void OnUserQuit(userrec* user, const std::string &message, const std::string &oper_message) + { + std::string* account; + user->GetExt("accountname", account); + if (account) + { + user->Shrink("accountname"); + delete account; + } + } + + // if the module is unloaded, tidy up all our dangling metadata + virtual void OnCleanup(int target_type, void* item) + { + if (target_type == TYPE_USER) + { + userrec* user = (userrec*)item; + std::string* account; + user->GetExt("accountname", account); + if (account) + { + user->Shrink("accountname"); + delete account; + } + } + } + + // Whenever the linking module receives metadata from another server and doesnt know what + // to do with it (of course, hence the 'meta') it calls this method, and it is up to each + // module in turn to figure out if this metadata key belongs to them, and what they want + // to do with it. + // In our case we're only sending a single string around, so we just construct a std::string. + // Some modules will probably get much more complex and format more detailed structs and classes + // in a textual way for sending over the link. + virtual void OnDecodeMetaData(int target_type, void* target, const std::string &extname, const std::string &extdata) + { + // check if its our metadata key, and its associated with a user + if ((target_type == TYPE_USER) && (extname == "accountname")) + { + userrec* dest = (userrec*)target; + + /* logging them out? */ + if (extdata.empty()) + { + std::string* account; + dest->GetExt("accountname", account); + if (account) + { + dest->Shrink("accountname"); + delete account; + } + } + else + { + // if they dont already have an accountname field, accept the remote server's + std::string* text; + if (!dest->GetExt("accountname", text)) + { + text = new std::string(extdata); + // remove any accidental leading/trailing spaces + trim(*text); + dest->Extend("accountname", text); + } + } + } + } + + virtual ~ModuleServicesAccount() + { + ServerInstance->Modes->DelMode(m1); + ServerInstance->Modes->DelMode(m2); + ServerInstance->Modes->DelMode(m3); + DELETE(m1); + DELETE(m2); + DELETE(m3); + } + + virtual Version GetVersion() + { + return Version(1,1,0,0,VF_COMMON|VF_VENDOR,API_VERSION); + } +}; + +MODULE_INIT(ModuleServicesAccount) diff --git a/src/modules/m_sethost.cpp b/src/modules/m_sethost.cpp index 833f8e684..e69a944e7 100644 --- a/src/modules/m_sethost.cpp +++ b/src/modules/m_sethost.cpp @@ -1 +1,108 @@ -/* +------------------------------------+
* | 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 "users.h"
#include "channels.h"
#include "modules.h"
/* $ModDesc: Provides support for the SETHOST command */
/** Handle /SETHOST
*/
class cmd_sethost : public command_t
{
private:
char* hostmap;
public:
cmd_sethost (InspIRCd* Instance, char* hmap) : command_t(Instance,"SETHOST",'o',1), hostmap(hmap)
{
this->source = "m_sethost.so";
syntax = "<new-hostname>";
}
CmdResult Handle (const char** parameters, int pcnt, userrec *user)
{
size_t len = 0;
for (const char* x = parameters[0]; *x; x++, len++)
{
if (!hostmap[(unsigned char)*x])
{
user->WriteServ("NOTICE "+std::string(user->nick)+" :*** SETHOST: Invalid characters in hostname");
return CMD_FAILURE;
}
}
if (len == 0)
{
user->WriteServ("NOTICE %s :*** SETHOST: Host must be specified", user->nick);
return CMD_FAILURE;
}
if (len > 64)
{
user->WriteServ("NOTICE %s :*** SETHOST: Host too long",user->nick);
return CMD_FAILURE;
}
if (user->ChangeDisplayedHost(parameters[0]))
{
ServerInstance->WriteOpers(std::string(user->nick)+" used SETHOST to change their displayed host to "+user->dhost);
return CMD_SUCCESS;
}
return CMD_FAILURE;
}
};
class ModuleSetHost : public Module
{
cmd_sethost* mycommand;
char hostmap[256];
public:
ModuleSetHost(InspIRCd* Me)
: Module(Me)
{
OnRehash(NULL,"");
mycommand = new cmd_sethost(ServerInstance, hostmap);
ServerInstance->AddCommand(mycommand);
}
void Implements(char* List)
{
List[I_OnRehash] = 1;
}
void OnRehash(userrec* user, const std::string ¶meter)
{
ConfigReader Conf(ServerInstance);
std::string hmap = Conf.ReadValue("hostname", "charmap", 0);
if (hmap.empty())
hmap = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz.-_/0123456789";
memset(&hostmap, 0, 255);
for (std::string::iterator n = hmap.begin(); n != hmap.end(); n++)
hostmap[(unsigned char)*n] = 1;
}
virtual ~ModuleSetHost()
{
}
virtual Version GetVersion()
{
return Version(1,1,0,1,VF_VENDOR,API_VERSION);
}
};
MODULE_INIT(ModuleSetHost)
\ No newline at end of file +/* +------------------------------------+ + * | 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 "users.h" +#include "channels.h" +#include "modules.h" + +/* $ModDesc: Provides support for the SETHOST command */ + +/** Handle /SETHOST + */ +class cmd_sethost : public command_t +{ + private: + char* hostmap; + public: + cmd_sethost (InspIRCd* Instance, char* hmap) : command_t(Instance,"SETHOST",'o',1), hostmap(hmap) + { + this->source = "m_sethost.so"; + syntax = "<new-hostname>"; + } + + CmdResult Handle (const char** parameters, int pcnt, userrec *user) + { + size_t len = 0; + for (const char* x = parameters[0]; *x; x++, len++) + { + if (!hostmap[(unsigned char)*x]) + { + user->WriteServ("NOTICE "+std::string(user->nick)+" :*** SETHOST: Invalid characters in hostname"); + return CMD_FAILURE; + } + } + if (len == 0) + { + user->WriteServ("NOTICE %s :*** SETHOST: Host must be specified", user->nick); + return CMD_FAILURE; + } + if (len > 64) + { + user->WriteServ("NOTICE %s :*** SETHOST: Host too long",user->nick); + return CMD_FAILURE; + } + if (user->ChangeDisplayedHost(parameters[0])) + { + ServerInstance->WriteOpers(std::string(user->nick)+" used SETHOST to change their displayed host to "+user->dhost); + return CMD_SUCCESS; + } + + return CMD_FAILURE; + } +}; + + +class ModuleSetHost : public Module +{ + cmd_sethost* mycommand; + char hostmap[256]; + public: + ModuleSetHost(InspIRCd* Me) + : Module(Me) + { + OnRehash(NULL,""); + mycommand = new cmd_sethost(ServerInstance, hostmap); + ServerInstance->AddCommand(mycommand); + } + + void Implements(char* List) + { + List[I_OnRehash] = 1; + } + + void OnRehash(userrec* user, const std::string ¶meter) + { + ConfigReader Conf(ServerInstance); + std::string hmap = Conf.ReadValue("hostname", "charmap", 0); + + if (hmap.empty()) + hmap = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz.-_/0123456789"; + + memset(&hostmap, 0, 255); + for (std::string::iterator n = hmap.begin(); n != hmap.end(); n++) + hostmap[(unsigned char)*n] = 1; + } + + virtual ~ModuleSetHost() + { + } + + virtual Version GetVersion() + { + return Version(1,1,0,1,VF_VENDOR,API_VERSION); + } + +}; + +MODULE_INIT(ModuleSetHost) diff --git a/src/modules/m_setident.cpp b/src/modules/m_setident.cpp index f512a1f59..3f33061cd 100644 --- a/src/modules/m_setident.cpp +++ b/src/modules/m_setident.cpp @@ -1 +1,83 @@ -/* +------------------------------------+
* | 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 "users.h"
#include "modules.h"
/* $ModDesc: Provides support for the SETIDENT command */
/** Handle /SETIDENT
*/
class cmd_setident : public command_t
{
public:
cmd_setident (InspIRCd* Instance) : command_t(Instance,"SETIDENT", 'o', 1)
{
this->source = "m_setident.so";
syntax = "<new-ident>";
}
CmdResult Handle(const char** parameters, int pcnt, userrec *user)
{
if (!*parameters[0])
{
user->WriteServ("NOTICE %s :*** SETIDENT: Ident must be specified", user->nick);
return CMD_FAILURE;
}
if (strlen(parameters[0]) > IDENTMAX)
{
user->WriteServ("NOTICE %s :*** SETIDENT: Ident is too long", user->nick);
return CMD_FAILURE;
}
if (!ServerInstance->IsIdent(parameters[0]))
{
user->WriteServ("NOTICE %s :*** SETIDENT: Invalid characters in ident", user->nick);
return CMD_FAILURE;
}
user->ChangeIdent(parameters[0]);
ServerInstance->WriteOpers("%s used SETIDENT to change their ident to '%s'", user->nick, user->ident);
return CMD_SUCCESS;
}
};
class ModuleSetIdent : public Module
{
cmd_setident* mycommand;
public:
ModuleSetIdent(InspIRCd* Me) : Module(Me)
{
mycommand = new cmd_setident(ServerInstance);
ServerInstance->AddCommand(mycommand);
}
virtual ~ModuleSetIdent()
{
}
virtual Version GetVersion()
{
return Version(1,1,0,0,VF_VENDOR,API_VERSION);
}
};
MODULE_INIT(ModuleSetIdent)
\ No newline at end of file +/* +------------------------------------+ + * | 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 "users.h" +#include "modules.h" + +/* $ModDesc: Provides support for the SETIDENT command */ + +/** Handle /SETIDENT + */ +class cmd_setident : public command_t +{ + public: + cmd_setident (InspIRCd* Instance) : command_t(Instance,"SETIDENT", 'o', 1) + { + this->source = "m_setident.so"; + syntax = "<new-ident>"; + } + + CmdResult Handle(const char** parameters, int pcnt, userrec *user) + { + if (!*parameters[0]) + { + user->WriteServ("NOTICE %s :*** SETIDENT: Ident must be specified", user->nick); + return CMD_FAILURE; + } + + if (strlen(parameters[0]) > IDENTMAX) + { + user->WriteServ("NOTICE %s :*** SETIDENT: Ident is too long", user->nick); + return CMD_FAILURE; + } + + if (!ServerInstance->IsIdent(parameters[0])) + { + user->WriteServ("NOTICE %s :*** SETIDENT: Invalid characters in ident", user->nick); + return CMD_FAILURE; + } + + user->ChangeIdent(parameters[0]); + ServerInstance->WriteOpers("%s used SETIDENT to change their ident to '%s'", user->nick, user->ident); + + return CMD_SUCCESS; + } +}; + + +class ModuleSetIdent : public Module +{ + cmd_setident* mycommand; + + public: + ModuleSetIdent(InspIRCd* Me) : Module(Me) + { + + mycommand = new cmd_setident(ServerInstance); + ServerInstance->AddCommand(mycommand); + } + + virtual ~ModuleSetIdent() + { + } + + virtual Version GetVersion() + { + return Version(1,1,0,0,VF_VENDOR,API_VERSION); + } + +}; + + +MODULE_INIT(ModuleSetIdent) diff --git a/src/modules/m_setidle.cpp b/src/modules/m_setidle.cpp index 917368d7b..e16369aa4 100644 --- a/src/modules/m_setidle.cpp +++ b/src/modules/m_setidle.cpp @@ -1 +1,74 @@ -/* +------------------------------------+
* | 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 "users.h"
#include "channels.h"
#include "modules.h"
/* $ModDesc: Allows opers to set their idle time */
/** Handle /SETIDLE
*/
class cmd_setidle : public command_t
{
public:
cmd_setidle (InspIRCd* Instance) : command_t(Instance,"SETIDLE", 'o', 1)
{
this->source = "m_setidle.so";
syntax = "<duration>";
}
CmdResult Handle (const char** parameters, int pcnt, userrec *user)
{
time_t idle = ServerInstance->Duration(parameters[0]);
if (idle < 1)
{
user->WriteServ("948 %s :Invalid idle time.",user->nick);
return CMD_FAILURE;
}
user->idle_lastmsg = (ServerInstance->Time() - idle);
// minor tweak - we cant have signon time shorter than our idle time!
if (user->signon > user->idle_lastmsg)
user->signon = user->idle_lastmsg;
ServerInstance->WriteOpers(std::string(user->nick)+" used SETIDLE to set their idle time to "+ConvToStr(idle)+" seconds");
user->WriteServ("944 %s :Idle time set.",user->nick);
return CMD_LOCALONLY;
}
};
class ModuleSetIdle : public Module
{
cmd_setidle* mycommand;
public:
ModuleSetIdle(InspIRCd* Me)
: Module(Me)
{
mycommand = new cmd_setidle(ServerInstance);
ServerInstance->AddCommand(mycommand);
}
virtual ~ModuleSetIdle()
{
}
virtual Version GetVersion()
{
return Version(1,1,0,1,VF_VENDOR,API_VERSION);
}
};
MODULE_INIT(ModuleSetIdle)
\ No newline at end of file +/* +------------------------------------+ + * | 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 "users.h" +#include "channels.h" +#include "modules.h" + +/* $ModDesc: Allows opers to set their idle time */ + +/** Handle /SETIDLE + */ +class cmd_setidle : public command_t +{ + public: + cmd_setidle (InspIRCd* Instance) : command_t(Instance,"SETIDLE", 'o', 1) + { + this->source = "m_setidle.so"; + syntax = "<duration>"; + } + + CmdResult Handle (const char** parameters, int pcnt, userrec *user) + { + time_t idle = ServerInstance->Duration(parameters[0]); + if (idle < 1) + { + user->WriteServ("948 %s :Invalid idle time.",user->nick); + return CMD_FAILURE; + } + user->idle_lastmsg = (ServerInstance->Time() - idle); + // minor tweak - we cant have signon time shorter than our idle time! + if (user->signon > user->idle_lastmsg) + user->signon = user->idle_lastmsg; + ServerInstance->WriteOpers(std::string(user->nick)+" used SETIDLE to set their idle time to "+ConvToStr(idle)+" seconds"); + user->WriteServ("944 %s :Idle time set.",user->nick); + + return CMD_LOCALONLY; + } +}; + + +class ModuleSetIdle : public Module +{ + cmd_setidle* mycommand; + public: + ModuleSetIdle(InspIRCd* Me) + : Module(Me) + { + + mycommand = new cmd_setidle(ServerInstance); + ServerInstance->AddCommand(mycommand); + } + + virtual ~ModuleSetIdle() + { + } + + virtual Version GetVersion() + { + return Version(1,1,0,1,VF_VENDOR,API_VERSION); + } +}; + +MODULE_INIT(ModuleSetIdle) diff --git a/src/modules/m_setname.cpp b/src/modules/m_setname.cpp index 3f525622f..586c6f84e 100644 --- a/src/modules/m_setname.cpp +++ b/src/modules/m_setname.cpp @@ -1 +1,80 @@ -/* +------------------------------------+
* | 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 "users.h"
#include "channels.h"
#include "modules.h"
/* $ModDesc: Provides support for the SETNAME command */
class cmd_setname : public command_t
{
public:
cmd_setname (InspIRCd* Instance) : command_t(Instance,"SETNAME", 0, 1)
{
this->source = "m_setname.so";
syntax = "<new-gecos>";
}
CmdResult Handle (const char** parameters, int pcnt, userrec *user)
{
if (!*parameters[0])
{
user->WriteServ("NOTICE %s :*** SETNAME: GECOS must be specified", user->nick);
return CMD_FAILURE;
}
if (strlen(parameters[0]) > MAXGECOS)
{
user->WriteServ("NOTICE %s :*** SETNAME: GECOS too long", user->nick);
return CMD_FAILURE;
}
if (user->ChangeName(parameters[0]))
{
ServerInstance->WriteOpers("%s used SETNAME to change their GECOS to %s", user->nick, parameters[0]);
return CMD_SUCCESS;
}
return CMD_SUCCESS;
}
};
class ModuleSetName : public Module
{
cmd_setname* mycommand;
public:
ModuleSetName(InspIRCd* Me)
: Module(Me)
{
mycommand = new cmd_setname(ServerInstance);
ServerInstance->AddCommand(mycommand);
}
virtual ~ModuleSetName()
{
}
virtual Version GetVersion()
{
return Version(1,1,0,1,VF_VENDOR,API_VERSION);
}
};
MODULE_INIT(ModuleSetName)
\ No newline at end of file +/* +------------------------------------+ + * | 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 "users.h" +#include "channels.h" +#include "modules.h" + +/* $ModDesc: Provides support for the SETNAME command */ + + + +class cmd_setname : public command_t +{ + public: + cmd_setname (InspIRCd* Instance) : command_t(Instance,"SETNAME", 0, 1) + { + this->source = "m_setname.so"; + syntax = "<new-gecos>"; + } + + CmdResult Handle (const char** parameters, int pcnt, userrec *user) + { + if (!*parameters[0]) + { + user->WriteServ("NOTICE %s :*** SETNAME: GECOS must be specified", user->nick); + return CMD_FAILURE; + } + + if (strlen(parameters[0]) > MAXGECOS) + { + user->WriteServ("NOTICE %s :*** SETNAME: GECOS too long", user->nick); + return CMD_FAILURE; + } + + if (user->ChangeName(parameters[0])) + { + ServerInstance->WriteOpers("%s used SETNAME to change their GECOS to %s", user->nick, parameters[0]); + return CMD_SUCCESS; + } + + return CMD_SUCCESS; + } +}; + + +class ModuleSetName : public Module +{ + cmd_setname* mycommand; + public: + ModuleSetName(InspIRCd* Me) + : Module(Me) + { + + mycommand = new cmd_setname(ServerInstance); + ServerInstance->AddCommand(mycommand); + } + + virtual ~ModuleSetName() + { + } + + virtual Version GetVersion() + { + return Version(1,1,0,1,VF_VENDOR,API_VERSION); + } + +}; + +MODULE_INIT(ModuleSetName) diff --git a/src/modules/m_sha256.cpp b/src/modules/m_sha256.cpp index 0dfef40b9..547e7655c 100644 --- a/src/modules/m_sha256.cpp +++ b/src/modules/m_sha256.cpp @@ -1 +1,296 @@ -/* +------------------------------------+
* | 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.
*
* ---------------------------------------------------
*/
/* m_sha256 - Based on m_opersha256 written by Special <john@yarbbles.com>
* Modified and improved by Craig Edwards, December 2006.
*
*
* FIPS 180-2 SHA-224/256/384/512 implementation
* Last update: 05/23/2005
* Issue date: 04/30/2005
*
* Copyright (C) 2005 Olivier Gay <olivier.gay@a3.epfl.ch>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the project nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
/* $ModDesc: Allows for SHA-256 encrypted oper passwords */
/* $ModDep: m_hash.h */
#include "inspircd.h"
#ifdef HAS_STDINT
#include <stdint.h>
#endif
#include "users.h"
#include "channels.h"
#include "modules.h"
#include "m_hash.h"
#ifndef HAS_STDINT
typedef unsigned int uint32_t;
#endif
/** An sha 256 context, used by m_opersha256
*/
class SHA256Context : public classbase
{
public:
unsigned int tot_len;
unsigned int len;
unsigned char block[2 * SHA256_BLOCK_SIZE];
uint32_t h[8];
};
#define SHFR(x, n) (x >> n)
#define ROTR(x, n) ((x >> n) | (x << ((sizeof(x) << 3) - n)))
#define ROTL(x, n) ((x << n) | (x >> ((sizeof(x) << 3) - n)))
#define CH(x, y, z) ((x & y) ^ (~x & z))
#define MAJ(x, y, z) ((x & y) ^ (x & z) ^ (y & z))
#define SHA256_F1(x) (ROTR(x, 2) ^ ROTR(x, 13) ^ ROTR(x, 22))
#define SHA256_F2(x) (ROTR(x, 6) ^ ROTR(x, 11) ^ ROTR(x, 25))
#define SHA256_F3(x) (ROTR(x, 7) ^ ROTR(x, 18) ^ SHFR(x, 3))
#define SHA256_F4(x) (ROTR(x, 17) ^ ROTR(x, 19) ^ SHFR(x, 10))
#define UNPACK32(x, str) \
{ \
*((str) + 3) = (uint8_t) ((x) ); \
*((str) + 2) = (uint8_t) ((x) >> 8); \
*((str) + 1) = (uint8_t) ((x) >> 16); \
*((str) + 0) = (uint8_t) ((x) >> 24); \
}
#define PACK32(str, x) \
{ \
*(x) = ((uint32_t) *((str) + 3) ) \
| ((uint32_t) *((str) + 2) << 8) \
| ((uint32_t) *((str) + 1) << 16) \
| ((uint32_t) *((str) + 0) << 24); \
}
/* Macros used for loops unrolling */
#define SHA256_SCR(i) \
{ \
w[i] = SHA256_F4(w[i - 2]) + w[i - 7] \
+ SHA256_F3(w[i - 15]) + w[i - 16]; \
}
const unsigned int sha256_h0[8] =
{
0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a,
0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19
};
uint32_t sha256_k[64] =
{
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5,
0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3,
0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc,
0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7,
0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13,
0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3,
0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5,
0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208,
0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2
};
class ModuleSHA256 : public Module
{
void SHA256Init(SHA256Context *ctx, const unsigned int* key)
{
if (key)
{
for (int i = 0; i < 8; i++)
ctx->h[i] = key[i];
}
else
{
for (int i = 0; i < 8; i++)
ctx->h[i] = sha256_h0[i];
}
ctx->len = 0;
ctx->tot_len = 0;
}
void SHA256Transform(SHA256Context *ctx, unsigned char *message, unsigned int block_nb)
{
uint32_t w[64];
uint32_t wv[8];
unsigned char *sub_block;
for (unsigned int i = 1; i <= block_nb; i++)
{
int j;
sub_block = message + ((i - 1) << 6);
for (j = 0; j < 16; j++)
PACK32(&sub_block[j << 2], &w[j]);
for (j = 16; j < 64; j++)
SHA256_SCR(j);
for (j = 0; j < 8; j++)
wv[j] = ctx->h[j];
for (j = 0; j < 64; j++)
{
uint32_t t1 = wv[7] + SHA256_F2(wv[4]) + CH(wv[4], wv[5], wv[6]) + sha256_k[j] + w[j];
uint32_t t2 = SHA256_F1(wv[0]) + MAJ(wv[0], wv[1], wv[2]);
wv[7] = wv[6];
wv[6] = wv[5];
wv[5] = wv[4];
wv[4] = wv[3] + t1;
wv[3] = wv[2];
wv[2] = wv[1];
wv[1] = wv[0];
wv[0] = t1 + t2;
}
for (j = 0; j < 8; j++)
ctx->h[j] += wv[j];
}
}
void SHA256Update(SHA256Context *ctx, unsigned char *message, unsigned int len)
{
unsigned int rem_len = SHA256_BLOCK_SIZE - ctx->len;
memcpy(&ctx->block[ctx->len], message, rem_len);
if (ctx->len + len < SHA256_BLOCK_SIZE)
{
ctx->len += len;
return;
}
unsigned int new_len = len - rem_len;
unsigned int block_nb = new_len / SHA256_BLOCK_SIZE;
unsigned char *shifted_message = message + rem_len;
SHA256Transform(ctx, ctx->block, 1);
SHA256Transform(ctx, shifted_message, block_nb);
rem_len = new_len % SHA256_BLOCK_SIZE;
memcpy(ctx->block, &shifted_message[block_nb << 6],rem_len);
ctx->len = rem_len;
ctx->tot_len += (block_nb + 1) << 6;
}
void SHA256Final(SHA256Context *ctx, unsigned char *digest)
{
unsigned int block_nb = (1 + ((SHA256_BLOCK_SIZE - 9) < (ctx->len % SHA256_BLOCK_SIZE)));
unsigned int len_b = (ctx->tot_len + ctx->len) << 3;
unsigned int pm_len = block_nb << 6;
memset(ctx->block + ctx->len, 0, pm_len - ctx->len);
ctx->block[ctx->len] = 0x80;
UNPACK32(len_b, ctx->block + pm_len - 4);
SHA256Transform(ctx, ctx->block, block_nb);
for (int i = 0 ; i < 8; i++)
UNPACK32(ctx->h[i], &digest[i << 2]);
}
void SHA256(const char *src, char *dest, int len, const char* hxc, const unsigned int* key = NULL)
{
// Generate the hash
unsigned char bytehash[SHA256_DIGEST_SIZE];
SHA256Context ctx;
SHA256Init(&ctx, key);
SHA256Update(&ctx, (unsigned char *)src, (unsigned int)len);
SHA256Final(&ctx, bytehash);
// Convert it to hex
for (int i = 0, j = 0; i < SHA256_DIGEST_SIZE; i++)
{
dest[j++] = hxc[bytehash[i] / 16];
dest[j++] = hxc[bytehash[i] % 16];
dest[j] = '\0';
}
}
unsigned int* key;
char* chars;
public:
ModuleSHA256(InspIRCd* Me) : Module(Me), key(NULL), chars(NULL)
{
ServerInstance->PublishInterface("HashRequest", this);
}
virtual ~ModuleSHA256()
{
ServerInstance->UnpublishInterface("HashRequest", this);
}
void Implements(char *List)
{
List[I_OnRequest] = 1;
}
virtual char* OnRequest(Request* request)
{
HashRequest* SHA = (HashRequest*)request;
if (strcmp("KEY", request->GetId()) == 0)
{
this->key = (unsigned int*)SHA->GetKeyData();
}
else if (strcmp("HEX", request->GetId()) == 0)
{
this->chars = (char*)SHA->GetOutputs();
}
else if (strcmp("SUM", request->GetId()) == 0)
{
static char data[MAXBUF];
SHA256((const char*)SHA->GetHashData(), data, strlen(SHA->GetHashData()), chars ? chars : "0123456789abcdef", key);
return data;
}
else if (strcmp("NAME", request->GetId()) == 0)
{
return "sha256";
}
else if (strcmp("RESET", request->GetId()) == 0)
{
this->chars = NULL;
this->key = NULL;
}
return NULL;
}
virtual Version GetVersion()
{
return Version(1, 1, 0, 1, VF_VENDOR|VF_SERVICEPROVIDER, API_VERSION);
}
};
MODULE_INIT(ModuleSHA256)
\ No newline at end of file +/* +------------------------------------+ + * | 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. + * + * --------------------------------------------------- + */ + +/* m_sha256 - Based on m_opersha256 written by Special <john@yarbbles.com> + * Modified and improved by Craig Edwards, December 2006. + * + * + * FIPS 180-2 SHA-224/256/384/512 implementation + * Last update: 05/23/2005 + * Issue date: 04/30/2005 + * + * Copyright (C) 2005 Olivier Gay <olivier.gay@a3.epfl.ch> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* $ModDesc: Allows for SHA-256 encrypted oper passwords */ +/* $ModDep: m_hash.h */ + +#include "inspircd.h" +#ifdef HAS_STDINT +#include <stdint.h> +#endif +#include "users.h" +#include "channels.h" +#include "modules.h" +#include "m_hash.h" + +#ifndef HAS_STDINT +typedef unsigned int uint32_t; +#endif + +/** An sha 256 context, used by m_opersha256 + */ +class SHA256Context : public classbase +{ + public: + unsigned int tot_len; + unsigned int len; + unsigned char block[2 * SHA256_BLOCK_SIZE]; + uint32_t h[8]; +}; + +#define SHFR(x, n) (x >> n) +#define ROTR(x, n) ((x >> n) | (x << ((sizeof(x) << 3) - n))) +#define ROTL(x, n) ((x << n) | (x >> ((sizeof(x) << 3) - n))) +#define CH(x, y, z) ((x & y) ^ (~x & z)) +#define MAJ(x, y, z) ((x & y) ^ (x & z) ^ (y & z)) + +#define SHA256_F1(x) (ROTR(x, 2) ^ ROTR(x, 13) ^ ROTR(x, 22)) +#define SHA256_F2(x) (ROTR(x, 6) ^ ROTR(x, 11) ^ ROTR(x, 25)) +#define SHA256_F3(x) (ROTR(x, 7) ^ ROTR(x, 18) ^ SHFR(x, 3)) +#define SHA256_F4(x) (ROTR(x, 17) ^ ROTR(x, 19) ^ SHFR(x, 10)) + +#define UNPACK32(x, str) \ +{ \ + *((str) + 3) = (uint8_t) ((x) ); \ + *((str) + 2) = (uint8_t) ((x) >> 8); \ + *((str) + 1) = (uint8_t) ((x) >> 16); \ + *((str) + 0) = (uint8_t) ((x) >> 24); \ +} + +#define PACK32(str, x) \ +{ \ + *(x) = ((uint32_t) *((str) + 3) ) \ + | ((uint32_t) *((str) + 2) << 8) \ + | ((uint32_t) *((str) + 1) << 16) \ + | ((uint32_t) *((str) + 0) << 24); \ +} + +/* Macros used for loops unrolling */ + +#define SHA256_SCR(i) \ +{ \ + w[i] = SHA256_F4(w[i - 2]) + w[i - 7] \ + + SHA256_F3(w[i - 15]) + w[i - 16]; \ +} + +const unsigned int sha256_h0[8] = +{ + 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, + 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19 +}; + +uint32_t sha256_k[64] = +{ + 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, + 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, + 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, + 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, + 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, + 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, + 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, + 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, + 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, + 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, + 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, + 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, + 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, + 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, + 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, + 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2 +}; + +class ModuleSHA256 : public Module +{ + void SHA256Init(SHA256Context *ctx, const unsigned int* key) + { + if (key) + { + for (int i = 0; i < 8; i++) + ctx->h[i] = key[i]; + } + else + { + for (int i = 0; i < 8; i++) + ctx->h[i] = sha256_h0[i]; + } + ctx->len = 0; + ctx->tot_len = 0; + } + + void SHA256Transform(SHA256Context *ctx, unsigned char *message, unsigned int block_nb) + { + uint32_t w[64]; + uint32_t wv[8]; + unsigned char *sub_block; + for (unsigned int i = 1; i <= block_nb; i++) + { + int j; + sub_block = message + ((i - 1) << 6); + + for (j = 0; j < 16; j++) + PACK32(&sub_block[j << 2], &w[j]); + for (j = 16; j < 64; j++) + SHA256_SCR(j); + for (j = 0; j < 8; j++) + wv[j] = ctx->h[j]; + for (j = 0; j < 64; j++) + { + uint32_t t1 = wv[7] + SHA256_F2(wv[4]) + CH(wv[4], wv[5], wv[6]) + sha256_k[j] + w[j]; + uint32_t t2 = SHA256_F1(wv[0]) + MAJ(wv[0], wv[1], wv[2]); + wv[7] = wv[6]; + wv[6] = wv[5]; + wv[5] = wv[4]; + wv[4] = wv[3] + t1; + wv[3] = wv[2]; + wv[2] = wv[1]; + wv[1] = wv[0]; + wv[0] = t1 + t2; + } + for (j = 0; j < 8; j++) + ctx->h[j] += wv[j]; + } + } + + void SHA256Update(SHA256Context *ctx, unsigned char *message, unsigned int len) + { + unsigned int rem_len = SHA256_BLOCK_SIZE - ctx->len; + memcpy(&ctx->block[ctx->len], message, rem_len); + if (ctx->len + len < SHA256_BLOCK_SIZE) + { + ctx->len += len; + return; + } + unsigned int new_len = len - rem_len; + unsigned int block_nb = new_len / SHA256_BLOCK_SIZE; + unsigned char *shifted_message = message + rem_len; + SHA256Transform(ctx, ctx->block, 1); + SHA256Transform(ctx, shifted_message, block_nb); + rem_len = new_len % SHA256_BLOCK_SIZE; + memcpy(ctx->block, &shifted_message[block_nb << 6],rem_len); + ctx->len = rem_len; + ctx->tot_len += (block_nb + 1) << 6; + } + + void SHA256Final(SHA256Context *ctx, unsigned char *digest) + { + unsigned int block_nb = (1 + ((SHA256_BLOCK_SIZE - 9) < (ctx->len % SHA256_BLOCK_SIZE))); + unsigned int len_b = (ctx->tot_len + ctx->len) << 3; + unsigned int pm_len = block_nb << 6; + memset(ctx->block + ctx->len, 0, pm_len - ctx->len); + ctx->block[ctx->len] = 0x80; + UNPACK32(len_b, ctx->block + pm_len - 4); + SHA256Transform(ctx, ctx->block, block_nb); + for (int i = 0 ; i < 8; i++) + UNPACK32(ctx->h[i], &digest[i << 2]); + } + + void SHA256(const char *src, char *dest, int len, const char* hxc, const unsigned int* key = NULL) + { + // Generate the hash + unsigned char bytehash[SHA256_DIGEST_SIZE]; + SHA256Context ctx; + SHA256Init(&ctx, key); + SHA256Update(&ctx, (unsigned char *)src, (unsigned int)len); + SHA256Final(&ctx, bytehash); + // Convert it to hex + for (int i = 0, j = 0; i < SHA256_DIGEST_SIZE; i++) + { + dest[j++] = hxc[bytehash[i] / 16]; + dest[j++] = hxc[bytehash[i] % 16]; + dest[j] = '\0'; + } + } + + unsigned int* key; + char* chars; + + public: + + ModuleSHA256(InspIRCd* Me) : Module(Me), key(NULL), chars(NULL) + { + ServerInstance->PublishInterface("HashRequest", this); + } + + virtual ~ModuleSHA256() + { + ServerInstance->UnpublishInterface("HashRequest", this); + } + + void Implements(char *List) + { + List[I_OnRequest] = 1; + } + + virtual char* OnRequest(Request* request) + { + HashRequest* SHA = (HashRequest*)request; + if (strcmp("KEY", request->GetId()) == 0) + { + this->key = (unsigned int*)SHA->GetKeyData(); + } + else if (strcmp("HEX", request->GetId()) == 0) + { + this->chars = (char*)SHA->GetOutputs(); + } + else if (strcmp("SUM", request->GetId()) == 0) + { + static char data[MAXBUF]; + SHA256((const char*)SHA->GetHashData(), data, strlen(SHA->GetHashData()), chars ? chars : "0123456789abcdef", key); + return data; + } + else if (strcmp("NAME", request->GetId()) == 0) + { + return "sha256"; + } + else if (strcmp("RESET", request->GetId()) == 0) + { + this->chars = NULL; + this->key = NULL; + } + return NULL; + } + + virtual Version GetVersion() + { + return Version(1, 1, 0, 1, VF_VENDOR|VF_SERVICEPROVIDER, API_VERSION); + } +}; + +MODULE_INIT(ModuleSHA256) + diff --git a/src/modules/m_showwhois.cpp b/src/modules/m_showwhois.cpp index 676962818..cb6a0ffb0 100644 --- a/src/modules/m_showwhois.cpp +++ b/src/modules/m_showwhois.cpp @@ -1 +1,109 @@ -/* +------------------------------------+
* | 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 "users.h"
#include "channels.h"
#include "modules.h"
/* $ModDesc: Allows opers to set +W to see when a user uses WHOIS on them */
/** Handle user mode +W
*/
class SeeWhois : public ModeHandler
{
public:
SeeWhois(InspIRCd* Instance) : ModeHandler(Instance, 'W', 0, 0, false, MODETYPE_USER, true) { }
ModeAction OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string ¶meter, bool adding)
{
/* Only opers can change other users modes */
if (source != dest)
return MODEACTION_DENY;
if (adding)
{
if (!dest->IsModeSet('W'))
{
dest->SetMode('W',true);
return MODEACTION_ALLOW;
}
}
else
{
if (dest->IsModeSet('W'))
{
dest->SetMode('W',false);
return MODEACTION_ALLOW;
}
}
return MODEACTION_DENY;
}
};
class ModuleShowwhois : public Module
{
SeeWhois* sw;
public:
ModuleShowwhois(InspIRCd* Me) : Module(Me)
{
sw = new SeeWhois(ServerInstance);
if (!ServerInstance->AddMode(sw, 'W'))
throw ModuleException("Could not add new modes!");
}
~ModuleShowwhois()
{
ServerInstance->Modes->DelMode(sw);
DELETE(sw);
}
void Implements(char* List)
{
List[I_OnWhois] = 1;
}
virtual Version GetVersion()
{
return Version(1,1,0,3,VF_COMMON|VF_VENDOR,API_VERSION);
}
virtual void OnWhois(userrec* source, userrec* dest)
{
if ((dest->IsModeSet('W')) && (source != dest))
{
if (IS_LOCAL(dest))
{
dest->WriteServ("NOTICE %s :*** %s (%s@%s) did a /whois on you.",dest->nick,source->nick,source->ident,source->host);
}
else
{
std::deque<std::string> params;
params.push_back(dest->nick);
std::string msg = ":";
msg = msg + dest->server + " NOTICE " + dest->nick + " :*** " + source->nick + " (" + source->ident + "@" + source->host + ") did a /whois on you.";
params.push_back(msg);
Event ev((char *) ¶ms, NULL, "send_push");
ev.Send(ServerInstance);
}
}
}
};
MODULE_INIT(ModuleShowwhois)
\ No newline at end of file +/* +------------------------------------+ + * | 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 "users.h" +#include "channels.h" +#include "modules.h" + +/* $ModDesc: Allows opers to set +W to see when a user uses WHOIS on them */ + +/** Handle user mode +W + */ +class SeeWhois : public ModeHandler +{ + public: + SeeWhois(InspIRCd* Instance) : ModeHandler(Instance, 'W', 0, 0, false, MODETYPE_USER, true) { } + + ModeAction OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string ¶meter, bool adding) + { + /* Only opers can change other users modes */ + if (source != dest) + return MODEACTION_DENY; + + if (adding) + { + if (!dest->IsModeSet('W')) + { + dest->SetMode('W',true); + return MODEACTION_ALLOW; + } + } + else + { + if (dest->IsModeSet('W')) + { + dest->SetMode('W',false); + return MODEACTION_ALLOW; + } + } + + return MODEACTION_DENY; + } +}; + +class ModuleShowwhois : public Module +{ + + SeeWhois* sw; + + public: + + ModuleShowwhois(InspIRCd* Me) : Module(Me) + { + + sw = new SeeWhois(ServerInstance); + if (!ServerInstance->AddMode(sw, 'W')) + throw ModuleException("Could not add new modes!"); + } + + ~ModuleShowwhois() + { + ServerInstance->Modes->DelMode(sw); + DELETE(sw); + } + + void Implements(char* List) + { + List[I_OnWhois] = 1; + } + + virtual Version GetVersion() + { + return Version(1,1,0,3,VF_COMMON|VF_VENDOR,API_VERSION); + } + + virtual void OnWhois(userrec* source, userrec* dest) + { + if ((dest->IsModeSet('W')) && (source != dest)) + { + if (IS_LOCAL(dest)) + { + dest->WriteServ("NOTICE %s :*** %s (%s@%s) did a /whois on you.",dest->nick,source->nick,source->ident,source->host); + } + else + { + std::deque<std::string> params; + params.push_back(dest->nick); + std::string msg = ":"; + msg = msg + dest->server + " NOTICE " + dest->nick + " :*** " + source->nick + " (" + source->ident + "@" + source->host + ") did a /whois on you."; + params.push_back(msg); + Event ev((char *) ¶ms, NULL, "send_push"); + ev.Send(ServerInstance); + } + } + } + +}; + +MODULE_INIT(ModuleShowwhois) diff --git a/src/modules/m_silence.cpp b/src/modules/m_silence.cpp index 3becb06f2..b05689056 100644 --- a/src/modules/m_silence.cpp +++ b/src/modules/m_silence.cpp @@ -1 +1,215 @@ -/* +------------------------------------+
* | 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 "users.h"
#include "channels.h"
#include "modules.h"
#include "hashcomp.h"
#include "wildcard.h"
/* $ModDesc: Provides support for the /SILENCE command */
// This typedef holds a silence list. Each user may or may not have a
// silencelist, if a silence list is empty for a user, he/she does not
// have one of these structures associated with their user record.
typedef std::map<irc::string, time_t> silencelist;
class cmd_silence : public command_t
{
unsigned int& maxsilence;
public:
cmd_silence (InspIRCd* Instance, unsigned int &max) : command_t(Instance,"SILENCE", 0, 0), maxsilence(max)
{
this->source = "m_silence.so";
syntax = "{[+|-]<mask>}";
}
CmdResult Handle (const char** parameters, int pcnt, userrec *user)
{
if (!pcnt)
{
// no parameters, show the current silence list.
// Use Extensible::GetExt to fetch the silence list
silencelist* sl;
user->GetExt("silence_list", sl);
// if the user has a silence list associated with their user record, show it
if (sl)
{
for (silencelist::const_iterator c = sl->begin(); c != sl->end(); c++)
{
user->WriteServ("271 %s %s %s :%lu",user->nick, user->nick, c->first.c_str(), (unsigned long)c->second);
}
}
user->WriteServ("272 %s :End of Silence List",user->nick);
return CMD_SUCCESS;
}
else if (pcnt > 0)
{
// one or more parameters, add or delete entry from the list (only the first parameter is used)
std::string mask = parameters[0] + 1;
char action = *parameters[0];
if (!mask.length())
{
// 'SILENCE +' or 'SILENCE -', assume *!*@*
mask = "*!*@*";
}
ModeParser::CleanMask(mask);
if (action == '-')
{
// fetch their silence list
silencelist* sl;
user->GetExt("silence_list", sl);
// does it contain any entries and does it exist?
if (sl)
{
silencelist::iterator i = sl->find(mask.c_str());
if (i != sl->end())
{
sl->erase(i);
user->WriteServ("950 %s %s :Removed %s from silence list",user->nick, user->nick, mask.c_str());
if (!sl->size())
{
// tidy up -- if a user's list is empty, theres no use having it
// hanging around in the user record.
DELETE(sl);
user->Shrink("silence_list");
}
}
else
user->WriteServ("952 %s %s :%s does not exist on your silence list",user->nick, user->nick, mask.c_str());
}
}
else if (action == '+')
{
// fetch the user's current silence list
silencelist* sl;
user->GetExt("silence_list", sl);
// what, they dont have one??? WE'RE ALL GONNA DIE! ...no, we just create an empty one.
if (!sl)
{
sl = new silencelist;
user->Extend("silence_list", sl);
}
silencelist::iterator n = sl->find(mask.c_str());
if (n != sl->end())
{
user->WriteServ("952 %s %s :%s is already on your silence list",user->nick, user->nick, mask.c_str());
return CMD_FAILURE;
}
if (sl->size() >= maxsilence)
{
user->WriteServ("952 %s %s :Your silence list is full",user->nick, user->nick, mask.c_str());
return CMD_FAILURE;
}
sl->insert(std::make_pair<irc::string, time_t>(mask.c_str(), ServerInstance->Time()));
user->WriteServ("951 %s %s :Added %s to silence list",user->nick, user->nick, mask.c_str());
return CMD_SUCCESS;
}
}
return CMD_SUCCESS;
}
};
class ModuleSilence : public Module
{
cmd_silence* mycommand;
unsigned int maxsilence;
public:
ModuleSilence(InspIRCd* Me)
: Module(Me), maxsilence(32)
{
OnRehash(NULL, "");
mycommand = new cmd_silence(ServerInstance, maxsilence);
ServerInstance->AddCommand(mycommand);
}
void Implements(char* List)
{
List[I_OnRehash] = List[I_OnUserQuit] = List[I_On005Numeric] = List[I_OnUserPreNotice] = List[I_OnUserPreMessage] = 1;
}
virtual void OnRehash(userrec* user, const std::string ¶meter)
{
ConfigReader Conf(ServerInstance);
maxsilence = Conf.ReadInteger("silence", "maxentries", 0, true);
if (!maxsilence)
maxsilence = 32;
}
virtual void OnUserQuit(userrec* user, const std::string &reason, const std::string &oper_message)
{
// when the user quits tidy up any silence list they might have just to keep things tidy
// and to prevent a HONKING BIG MEMORY LEAK!
silencelist* sl;
user->GetExt("silence_list", sl);
if (sl)
{
DELETE(sl);
user->Shrink("silence_list");
}
}
virtual void On005Numeric(std::string &output)
{
// we don't really have a limit...
output = output + " SILENCE=" + ConvToStr(maxsilence);
}
virtual int OnUserPreNotice(userrec* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list)
{
// im not sure how unreal's silence operates but ours is sensible. It blocks notices and
// privmsgs from people on the silence list, directed privately at the user.
// channel messages are unaffected (ever tried to follow the flow of conversation in
// a channel when you've set an ignore on the two most talkative people?)
if ((target_type == TYPE_USER) && (IS_LOCAL(user)))
{
userrec* u = (userrec*)dest;
silencelist* sl;
u->GetExt("silence_list", sl);
if (sl)
{
for (silencelist::const_iterator c = sl->begin(); c != sl->end(); c++)
{
if (match(user->GetFullHost(), c->first.c_str()))
{
return 1;
}
}
}
}
return 0;
}
virtual int OnUserPreMessage(userrec* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list)
{
return OnUserPreNotice(user,dest,target_type,text,status,exempt_list);
}
virtual ~ModuleSilence()
{
}
virtual Version GetVersion()
{
return Version(1,1,0,1,VF_VENDOR,API_VERSION);
}
};
MODULE_INIT(ModuleSilence)
\ No newline at end of file +/* +------------------------------------+ + * | 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 "users.h" +#include "channels.h" +#include "modules.h" +#include "hashcomp.h" +#include "wildcard.h" + +/* $ModDesc: Provides support for the /SILENCE command */ + +// This typedef holds a silence list. Each user may or may not have a +// silencelist, if a silence list is empty for a user, he/she does not +// have one of these structures associated with their user record. +typedef std::map<irc::string, time_t> silencelist; + +class cmd_silence : public command_t +{ + unsigned int& maxsilence; + public: + cmd_silence (InspIRCd* Instance, unsigned int &max) : command_t(Instance,"SILENCE", 0, 0), maxsilence(max) + { + this->source = "m_silence.so"; + syntax = "{[+|-]<mask>}"; + } + + CmdResult Handle (const char** parameters, int pcnt, userrec *user) + { + if (!pcnt) + { + // no parameters, show the current silence list. + // Use Extensible::GetExt to fetch the silence list + silencelist* sl; + user->GetExt("silence_list", sl); + // if the user has a silence list associated with their user record, show it + if (sl) + { + for (silencelist::const_iterator c = sl->begin(); c != sl->end(); c++) + { + user->WriteServ("271 %s %s %s :%lu",user->nick, user->nick, c->first.c_str(), (unsigned long)c->second); + } + } + user->WriteServ("272 %s :End of Silence List",user->nick); + + return CMD_SUCCESS; + } + else if (pcnt > 0) + { + // one or more parameters, add or delete entry from the list (only the first parameter is used) + std::string mask = parameters[0] + 1; + char action = *parameters[0]; + + if (!mask.length()) + { + // 'SILENCE +' or 'SILENCE -', assume *!*@* + mask = "*!*@*"; + } + + ModeParser::CleanMask(mask); + + if (action == '-') + { + // fetch their silence list + silencelist* sl; + user->GetExt("silence_list", sl); + // does it contain any entries and does it exist? + if (sl) + { + silencelist::iterator i = sl->find(mask.c_str()); + if (i != sl->end()) + { + sl->erase(i); + user->WriteServ("950 %s %s :Removed %s from silence list",user->nick, user->nick, mask.c_str()); + if (!sl->size()) + { + // tidy up -- if a user's list is empty, theres no use having it + // hanging around in the user record. + DELETE(sl); + user->Shrink("silence_list"); + } + } + else + user->WriteServ("952 %s %s :%s does not exist on your silence list",user->nick, user->nick, mask.c_str()); + } + } + else if (action == '+') + { + // fetch the user's current silence list + silencelist* sl; + user->GetExt("silence_list", sl); + // what, they dont have one??? WE'RE ALL GONNA DIE! ...no, we just create an empty one. + if (!sl) + { + sl = new silencelist; + user->Extend("silence_list", sl); + } + silencelist::iterator n = sl->find(mask.c_str()); + if (n != sl->end()) + { + user->WriteServ("952 %s %s :%s is already on your silence list",user->nick, user->nick, mask.c_str()); + return CMD_FAILURE; + } + if (sl->size() >= maxsilence) + { + user->WriteServ("952 %s %s :Your silence list is full",user->nick, user->nick, mask.c_str()); + return CMD_FAILURE; + } + sl->insert(std::make_pair<irc::string, time_t>(mask.c_str(), ServerInstance->Time())); + user->WriteServ("951 %s %s :Added %s to silence list",user->nick, user->nick, mask.c_str()); + return CMD_SUCCESS; + } + } + return CMD_SUCCESS; + } +}; + +class ModuleSilence : public Module +{ + + cmd_silence* mycommand; + unsigned int maxsilence; + public: + + ModuleSilence(InspIRCd* Me) + : Module(Me), maxsilence(32) + { + OnRehash(NULL, ""); + mycommand = new cmd_silence(ServerInstance, maxsilence); + ServerInstance->AddCommand(mycommand); + } + + void Implements(char* List) + { + List[I_OnRehash] = List[I_OnUserQuit] = List[I_On005Numeric] = List[I_OnUserPreNotice] = List[I_OnUserPreMessage] = 1; + } + + virtual void OnRehash(userrec* user, const std::string ¶meter) + { + ConfigReader Conf(ServerInstance); + maxsilence = Conf.ReadInteger("silence", "maxentries", 0, true); + if (!maxsilence) + maxsilence = 32; + } + + virtual void OnUserQuit(userrec* user, const std::string &reason, const std::string &oper_message) + { + // when the user quits tidy up any silence list they might have just to keep things tidy + // and to prevent a HONKING BIG MEMORY LEAK! + silencelist* sl; + user->GetExt("silence_list", sl); + if (sl) + { + DELETE(sl); + user->Shrink("silence_list"); + } + } + + virtual void On005Numeric(std::string &output) + { + // we don't really have a limit... + output = output + " SILENCE=" + ConvToStr(maxsilence); + } + + virtual int OnUserPreNotice(userrec* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list) + { + // im not sure how unreal's silence operates but ours is sensible. It blocks notices and + // privmsgs from people on the silence list, directed privately at the user. + // channel messages are unaffected (ever tried to follow the flow of conversation in + // a channel when you've set an ignore on the two most talkative people?) + if ((target_type == TYPE_USER) && (IS_LOCAL(user))) + { + userrec* u = (userrec*)dest; + silencelist* sl; + u->GetExt("silence_list", sl); + if (sl) + { + for (silencelist::const_iterator c = sl->begin(); c != sl->end(); c++) + { + if (match(user->GetFullHost(), c->first.c_str())) + { + return 1; + } + } + } + } + return 0; + } + + virtual int OnUserPreMessage(userrec* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list) + { + return OnUserPreNotice(user,dest,target_type,text,status,exempt_list); + } + + virtual ~ModuleSilence() + { + } + + virtual Version GetVersion() + { + return Version(1,1,0,1,VF_VENDOR,API_VERSION); + } +}; + +MODULE_INIT(ModuleSilence) diff --git a/src/modules/m_silence_ext.cpp b/src/modules/m_silence_ext.cpp index 7b1588043..06eee9dd4 100644 --- a/src/modules/m_silence_ext.cpp +++ b/src/modules/m_silence_ext.cpp @@ -1 +1,372 @@ -/* +------------------------------------+
* | 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 "users.h"
#include "channels.h"
#include "modules.h"
#include "hashcomp.h"
#include "wildcard.h"
/* $ModDesc: Provides support for the /SILENCE command */
/* Improved drop-in replacement for the /SILENCE command
* syntax: /SILENCE [+|-]<mask> <p|c|i|n|t|a|x> as in <privatemessage|channelmessage|invites|privatenotice|channelnotice|all|exclude>
*
* example that blocks all except private messages
* /SILENCE +*!*@* a
* /SILENCE +*!*@* px
*
* example that blocks all invites except from channel services
* /SILENCE +*!*@* i
* /SILENCE +chanserv!services@chatters.net ix
*
* example that blocks some bad dude from private, notice and inviting you
* /SILENCE +*!kiddie@lamerz.net pin
*
* TODO: possibly have add and remove check for existing host and only modify flags according to
* what's been changed instead of having to remove first, then add if you want to change
* an entry.
*/
// pair of hostmask and flags
typedef std::pair<std::string, int> silenceset;
// deque list of pairs
typedef std::deque<silenceset> silencelist;
// intmasks for flags
static int SILENCE_PRIVATE = 0x0001; /* p private messages */
static int SILENCE_CHANNEL = 0x0002; /* c channel messages */
static int SILENCE_INVITE = 0x0004; /* i invites */
static int SILENCE_NOTICE = 0x0008; /* n notices */
static int SILENCE_CNOTICE = 0x0010; /* t channel notices */
static int SILENCE_ALL = 0x0020; /* a all, (pcint) */
static int SILENCE_EXCLUDE = 0x0040; /* x exclude this pattern */
class cmd_silence : public command_t
{
unsigned int& maxsilence;
public:
cmd_silence (InspIRCd* Instance, unsigned int &max) : command_t(Instance,"SILENCE", 0, 0), maxsilence(max)
{
this->source = "m_silence_ext.so";
syntax = "{[+|-]<mask> <p|c|i|n|t|a|x>}";
}
CmdResult Handle (const char** parameters, int pcnt, userrec *user)
{
if (!pcnt)
{
// no parameters, show the current silence list.
// Use Extensible::GetExt to fetch the silence list
silencelist* sl;
user->GetExt("silence_list", sl);
// if the user has a silence list associated with their user record, show it
if (sl)
{
for (silencelist::const_iterator c = sl->begin(); c != sl->end(); c++)
{
user->WriteServ("271 %s %s %s %s",user->nick, user->nick,c->first.c_str(), DecompPattern(c->second).c_str());
}
}
user->WriteServ("272 %s :End of Silence List",user->nick);
return CMD_LOCALONLY;
}
else if (pcnt > 0)
{
// one or more parameters, add or delete entry from the list (only the first parameter is used)
std::string mask = parameters[0] + 1;
char action = *parameters[0];
// Default is private and notice so clients do not break
int pattern = CompilePattern("pn");
// if pattern supplied, use it
if (pcnt > 1) {
pattern = CompilePattern(parameters[1]);
}
if (!mask.length())
{
// 'SILENCE +' or 'SILENCE -', assume *!*@*
mask = "*!*@*";
}
ModeParser::CleanMask(mask);
if (action == '-')
{
// fetch their silence list
silencelist* sl;
user->GetExt("silence_list", sl);
// does it contain any entries and does it exist?
if (sl)
{
for (silencelist::iterator i = sl->begin(); i != sl->end(); i++)
{
// search through for the item
irc::string listitem = i->first.c_str();
if (listitem == mask && i->second == pattern)
{
sl->erase(i);
user->WriteServ("950 %s %s :Removed %s %s from silence list",user->nick, user->nick, mask.c_str(), DecompPattern(pattern).c_str());
if (!sl->size())
{
DELETE(sl);
user->Shrink("silence_list");
}
break;
}
}
}
user->WriteServ("952 %s %s :%s %s does not exist on your silence list",user->nick, user->nick, mask.c_str(), DecompPattern(pattern).c_str());
}
else if (action == '+')
{
// fetch the user's current silence list
silencelist* sl;
user->GetExt("silence_list", sl);
// what, they dont have one??? WE'RE ALL GONNA DIE! ...no, we just create an empty one.
if (!sl)
{
sl = new silencelist;
user->Extend("silence_list", sl);
}
if (sl->size() > maxsilence)
{
user->WriteServ("952 %s %s :Your silence list is full",user->nick, user->nick);
return CMD_FAILURE;
}
for (silencelist::iterator n = sl->begin(); n != sl->end(); n++)
{
irc::string listitem = n->first.c_str();
if (listitem == mask && n->second == pattern)
{
user->WriteServ("952 %s %s :%s %s is already on your silence list",user->nick, user->nick, mask.c_str(), DecompPattern(pattern).c_str());
return CMD_FAILURE;
}
}
if (((pattern & SILENCE_EXCLUDE) > 0))
{
sl->push_front(silenceset(mask,pattern));
}
else
{
sl->push_back(silenceset(mask,pattern));
}
user->WriteServ("951 %s %s :Added %s %s to silence list",user->nick, user->nick, mask.c_str(), DecompPattern(pattern).c_str());
return CMD_LOCALONLY;
}
}
return CMD_LOCALONLY;
}
/* turn the nice human readable pattern into a mask */
int CompilePattern(const char* pattern)
{
int p = 0;
for (const char* n = pattern; *n; n++)
{
switch (*n)
{
case 'p':
p |= SILENCE_PRIVATE;
break;
case 'c':
p |= SILENCE_CHANNEL;
break;
case 'i':
p |= SILENCE_INVITE;
break;
case 'n':
p |= SILENCE_NOTICE;
break;
case 't':
p |= SILENCE_CNOTICE;
break;
case 'a':
p |= SILENCE_ALL;
break;
case 'x':
p |= SILENCE_EXCLUDE;
break;
default:
break;
}
}
return p;
}
/* turn the mask into a nice human readable format */
std::string DecompPattern (const int pattern)
{
std::string out;
if ((pattern & SILENCE_PRIVATE) > 0)
out += ",privatemessages";
if ((pattern & SILENCE_CHANNEL) > 0)
out += ",channelmessages";
if ((pattern & SILENCE_INVITE) > 0)
out += ",invites";
if ((pattern & SILENCE_NOTICE) > 0)
out += ",privatenotices";
if ((pattern & SILENCE_CNOTICE) > 0)
out += ",channelnotices";
if ((pattern & SILENCE_ALL) > 0)
out = ",all";
if ((pattern & SILENCE_EXCLUDE) > 0)
out += ",exclude";
return "<" + out.substr(1) + ">";
}
};
class ModuleSilence : public Module
{
cmd_silence* mycommand;
unsigned int maxsilence;
public:
ModuleSilence(InspIRCd* Me)
: Module(Me), maxsilence(32)
{
OnRehash(NULL, "");
mycommand = new cmd_silence(ServerInstance,maxsilence);
ServerInstance->AddCommand(mycommand);
}
virtual void OnRehash(userrec* user, const std::string ¶meter)
{
ConfigReader Conf(ServerInstance);
maxsilence = Conf.ReadInteger("silence", "maxentries", 0, true);
if (!maxsilence)
maxsilence = 32;
}
void Implements(char* List)
{
List[I_OnRehash] = List[I_OnBuildExemptList] = List[I_OnUserQuit] = List[I_On005Numeric] = List[I_OnUserPreNotice] = List[I_OnUserPreMessage] = List[I_OnUserPreInvite] = 1;
}
virtual void OnUserQuit(userrec* user, const std::string &reason, const std::string &oper_message)
{
// when the user quits tidy up any silence list they might have just to keep things tidy
silencelist* sl;
user->GetExt("silence_list", sl);
if (sl)
{
DELETE(sl);
user->Shrink("silence_list");
}
}
virtual void On005Numeric(std::string &output)
{
// we don't really have a limit...
output = output + " ESILENCE SILENCE=" + ConvToStr(maxsilence);
}
virtual void OnBuildExemptList(MessageType message_type, chanrec* chan, userrec* sender, char status, CUList &exempt_list)
{
int public_silence = (message_type == MSG_PRIVMSG ? SILENCE_CHANNEL : SILENCE_CNOTICE);
CUList *ulist;
switch (status)
{
case '@':
ulist = chan->GetOppedUsers();
break;
case '%':
ulist = chan->GetHalfoppedUsers();
break;
case '+':
ulist = chan->GetVoicedUsers();
break;
default:
ulist = chan->GetUsers();
break;
}
for (CUList::iterator i = ulist->begin(); i != ulist->end(); i++)
{
if (IS_LOCAL(i->first))
{
if (MatchPattern(i->first, sender, public_silence) == 1)
{
exempt_list[i->first] = i->first->nick;
}
}
}
}
virtual int PreText(userrec* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list, int silence_type)
{
if (!IS_LOCAL(user))
return 0;
if (target_type == TYPE_USER)
{
return MatchPattern((userrec*)dest, user, silence_type);
}
else if (target_type == TYPE_CHANNEL)
{
chanrec* chan = (chanrec*)dest;
if (chan)
{
this->OnBuildExemptList((silence_type == SILENCE_PRIVATE ? MSG_PRIVMSG : MSG_NOTICE), chan, user, status, exempt_list);
}
}
return 0;
}
virtual int OnUserPreMessage(userrec* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list)
{
return PreText(user, dest, target_type, text, status, exempt_list, SILENCE_PRIVATE);
}
virtual int OnUserPreNotice(userrec* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list)
{
return PreText(user, dest, target_type, text, status, exempt_list, SILENCE_NOTICE);
}
virtual int OnUserPreInvite(userrec* source,userrec* dest,chanrec* channel)
{
return MatchPattern(dest, source, SILENCE_INVITE);
}
int MatchPattern(userrec* dest, userrec* source, int pattern)
{
silencelist* sl;
dest->GetExt("silence_list", sl);
if (sl)
{
for (silencelist::const_iterator c = sl->begin(); c != sl->end(); c++)
{
if (((((c->second & pattern) > 0)) || ((c->second & SILENCE_ALL) > 0)) && (ServerInstance->MatchText(source->GetFullHost(), c->first)))
return !(((c->second & SILENCE_EXCLUDE) > 0));
}
}
return 0;
}
virtual ~ModuleSilence()
{
}
virtual Version GetVersion()
{
return Version(1,1,0,1,VF_VENDOR,API_VERSION);
}
};
MODULE_INIT(ModuleSilence)
\ No newline at end of file +/* +------------------------------------+ + * | 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 "users.h" +#include "channels.h" +#include "modules.h" +#include "hashcomp.h" +#include "wildcard.h" + +/* $ModDesc: Provides support for the /SILENCE command */ + +/* Improved drop-in replacement for the /SILENCE command + * syntax: /SILENCE [+|-]<mask> <p|c|i|n|t|a|x> as in <privatemessage|channelmessage|invites|privatenotice|channelnotice|all|exclude> + * + * example that blocks all except private messages + * /SILENCE +*!*@* a + * /SILENCE +*!*@* px + * + * example that blocks all invites except from channel services + * /SILENCE +*!*@* i + * /SILENCE +chanserv!services@chatters.net ix + * + * example that blocks some bad dude from private, notice and inviting you + * /SILENCE +*!kiddie@lamerz.net pin + * + * TODO: possibly have add and remove check for existing host and only modify flags according to + * what's been changed instead of having to remove first, then add if you want to change + * an entry. + */ + +// pair of hostmask and flags +typedef std::pair<std::string, int> silenceset; + +// deque list of pairs +typedef std::deque<silenceset> silencelist; + +// intmasks for flags +static int SILENCE_PRIVATE = 0x0001; /* p private messages */ +static int SILENCE_CHANNEL = 0x0002; /* c channel messages */ +static int SILENCE_INVITE = 0x0004; /* i invites */ +static int SILENCE_NOTICE = 0x0008; /* n notices */ +static int SILENCE_CNOTICE = 0x0010; /* t channel notices */ +static int SILENCE_ALL = 0x0020; /* a all, (pcint) */ +static int SILENCE_EXCLUDE = 0x0040; /* x exclude this pattern */ + + +class cmd_silence : public command_t +{ + unsigned int& maxsilence; + public: + cmd_silence (InspIRCd* Instance, unsigned int &max) : command_t(Instance,"SILENCE", 0, 0), maxsilence(max) + { + this->source = "m_silence_ext.so"; + syntax = "{[+|-]<mask> <p|c|i|n|t|a|x>}"; + } + + CmdResult Handle (const char** parameters, int pcnt, userrec *user) + { + if (!pcnt) + { + // no parameters, show the current silence list. + // Use Extensible::GetExt to fetch the silence list + silencelist* sl; + user->GetExt("silence_list", sl); + // if the user has a silence list associated with their user record, show it + if (sl) + { + for (silencelist::const_iterator c = sl->begin(); c != sl->end(); c++) + { + user->WriteServ("271 %s %s %s %s",user->nick, user->nick,c->first.c_str(), DecompPattern(c->second).c_str()); + } + } + user->WriteServ("272 %s :End of Silence List",user->nick); + + return CMD_LOCALONLY; + } + else if (pcnt > 0) + { + // one or more parameters, add or delete entry from the list (only the first parameter is used) + std::string mask = parameters[0] + 1; + char action = *parameters[0]; + // Default is private and notice so clients do not break + int pattern = CompilePattern("pn"); + + // if pattern supplied, use it + if (pcnt > 1) { + pattern = CompilePattern(parameters[1]); + } + + if (!mask.length()) + { + // 'SILENCE +' or 'SILENCE -', assume *!*@* + mask = "*!*@*"; + } + + ModeParser::CleanMask(mask); + + if (action == '-') + { + // fetch their silence list + silencelist* sl; + user->GetExt("silence_list", sl); + // does it contain any entries and does it exist? + if (sl) + { + for (silencelist::iterator i = sl->begin(); i != sl->end(); i++) + { + // search through for the item + irc::string listitem = i->first.c_str(); + if (listitem == mask && i->second == pattern) + { + sl->erase(i); + user->WriteServ("950 %s %s :Removed %s %s from silence list",user->nick, user->nick, mask.c_str(), DecompPattern(pattern).c_str()); + if (!sl->size()) + { + DELETE(sl); + user->Shrink("silence_list"); + } + break; + } + } + } + user->WriteServ("952 %s %s :%s %s does not exist on your silence list",user->nick, user->nick, mask.c_str(), DecompPattern(pattern).c_str()); + } + else if (action == '+') + { + // fetch the user's current silence list + silencelist* sl; + user->GetExt("silence_list", sl); + // what, they dont have one??? WE'RE ALL GONNA DIE! ...no, we just create an empty one. + if (!sl) + { + sl = new silencelist; + user->Extend("silence_list", sl); + } + if (sl->size() > maxsilence) + { + user->WriteServ("952 %s %s :Your silence list is full",user->nick, user->nick); + return CMD_FAILURE; + } + for (silencelist::iterator n = sl->begin(); n != sl->end(); n++) + { + irc::string listitem = n->first.c_str(); + if (listitem == mask && n->second == pattern) + { + user->WriteServ("952 %s %s :%s %s is already on your silence list",user->nick, user->nick, mask.c_str(), DecompPattern(pattern).c_str()); + return CMD_FAILURE; + } + } + if (((pattern & SILENCE_EXCLUDE) > 0)) + { + sl->push_front(silenceset(mask,pattern)); + } + else + { + sl->push_back(silenceset(mask,pattern)); + } + user->WriteServ("951 %s %s :Added %s %s to silence list",user->nick, user->nick, mask.c_str(), DecompPattern(pattern).c_str()); + return CMD_LOCALONLY; + } + } + return CMD_LOCALONLY; + } + + /* turn the nice human readable pattern into a mask */ + int CompilePattern(const char* pattern) + { + int p = 0; + for (const char* n = pattern; *n; n++) + { + switch (*n) + { + case 'p': + p |= SILENCE_PRIVATE; + break; + case 'c': + p |= SILENCE_CHANNEL; + break; + case 'i': + p |= SILENCE_INVITE; + break; + case 'n': + p |= SILENCE_NOTICE; + break; + case 't': + p |= SILENCE_CNOTICE; + break; + case 'a': + p |= SILENCE_ALL; + break; + case 'x': + p |= SILENCE_EXCLUDE; + break; + default: + break; + } + } + return p; + } + + /* turn the mask into a nice human readable format */ + std::string DecompPattern (const int pattern) + { + std::string out; + if ((pattern & SILENCE_PRIVATE) > 0) + out += ",privatemessages"; + if ((pattern & SILENCE_CHANNEL) > 0) + out += ",channelmessages"; + if ((pattern & SILENCE_INVITE) > 0) + out += ",invites"; + if ((pattern & SILENCE_NOTICE) > 0) + out += ",privatenotices"; + if ((pattern & SILENCE_CNOTICE) > 0) + out += ",channelnotices"; + if ((pattern & SILENCE_ALL) > 0) + out = ",all"; + if ((pattern & SILENCE_EXCLUDE) > 0) + out += ",exclude"; + return "<" + out.substr(1) + ">"; + } + +}; + +class ModuleSilence : public Module +{ + cmd_silence* mycommand; + unsigned int maxsilence; + public: + + ModuleSilence(InspIRCd* Me) + : Module(Me), maxsilence(32) + { + OnRehash(NULL, ""); + mycommand = new cmd_silence(ServerInstance,maxsilence); + ServerInstance->AddCommand(mycommand); + } + + virtual void OnRehash(userrec* user, const std::string ¶meter) + { + ConfigReader Conf(ServerInstance); + maxsilence = Conf.ReadInteger("silence", "maxentries", 0, true); + if (!maxsilence) + maxsilence = 32; + } + + void Implements(char* List) + { + List[I_OnRehash] = List[I_OnBuildExemptList] = List[I_OnUserQuit] = List[I_On005Numeric] = List[I_OnUserPreNotice] = List[I_OnUserPreMessage] = List[I_OnUserPreInvite] = 1; + } + + virtual void OnUserQuit(userrec* user, const std::string &reason, const std::string &oper_message) + { + // when the user quits tidy up any silence list they might have just to keep things tidy + silencelist* sl; + user->GetExt("silence_list", sl); + if (sl) + { + DELETE(sl); + user->Shrink("silence_list"); + } + } + + virtual void On005Numeric(std::string &output) + { + // we don't really have a limit... + output = output + " ESILENCE SILENCE=" + ConvToStr(maxsilence); + } + + virtual void OnBuildExemptList(MessageType message_type, chanrec* chan, userrec* sender, char status, CUList &exempt_list) + { + int public_silence = (message_type == MSG_PRIVMSG ? SILENCE_CHANNEL : SILENCE_CNOTICE); + CUList *ulist; + switch (status) + { + case '@': + ulist = chan->GetOppedUsers(); + break; + case '%': + ulist = chan->GetHalfoppedUsers(); + break; + case '+': + ulist = chan->GetVoicedUsers(); + break; + default: + ulist = chan->GetUsers(); + break; + } + + for (CUList::iterator i = ulist->begin(); i != ulist->end(); i++) + { + if (IS_LOCAL(i->first)) + { + if (MatchPattern(i->first, sender, public_silence) == 1) + { + exempt_list[i->first] = i->first->nick; + } + } + } + } + + virtual int PreText(userrec* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list, int silence_type) + { + if (!IS_LOCAL(user)) + return 0; + + if (target_type == TYPE_USER) + { + return MatchPattern((userrec*)dest, user, silence_type); + } + else if (target_type == TYPE_CHANNEL) + { + chanrec* chan = (chanrec*)dest; + if (chan) + { + this->OnBuildExemptList((silence_type == SILENCE_PRIVATE ? MSG_PRIVMSG : MSG_NOTICE), chan, user, status, exempt_list); + } + } + return 0; + } + + virtual int OnUserPreMessage(userrec* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list) + { + return PreText(user, dest, target_type, text, status, exempt_list, SILENCE_PRIVATE); + } + + virtual int OnUserPreNotice(userrec* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list) + { + return PreText(user, dest, target_type, text, status, exempt_list, SILENCE_NOTICE); + } + + virtual int OnUserPreInvite(userrec* source,userrec* dest,chanrec* channel) + { + return MatchPattern(dest, source, SILENCE_INVITE); + } + + int MatchPattern(userrec* dest, userrec* source, int pattern) + { + silencelist* sl; + dest->GetExt("silence_list", sl); + if (sl) + { + for (silencelist::const_iterator c = sl->begin(); c != sl->end(); c++) + { + if (((((c->second & pattern) > 0)) || ((c->second & SILENCE_ALL) > 0)) && (ServerInstance->MatchText(source->GetFullHost(), c->first))) + return !(((c->second & SILENCE_EXCLUDE) > 0)); + } + } + return 0; + } + + virtual ~ModuleSilence() + { + } + + virtual Version GetVersion() + { + return Version(1,1,0,1,VF_VENDOR,API_VERSION); + } +}; + +MODULE_INIT(ModuleSilence) diff --git a/src/modules/m_spanningtree/README b/src/modules/m_spanningtree/README index 76c678c1f..ff23e0381 100644 --- a/src/modules/m_spanningtree/README +++ b/src/modules/m_spanningtree/README @@ -1 +1,24 @@ -m_spanningtree
--------------
This directory contains all files required to build the m_spanningtree.so module.
Directories like this one starting with m_ and containing .cpp files are aggregated
together by the makefile to form a single .so, with the same name as the directory.
This directory contains the following files:
* handshaketimer.cpp Code for detecting end of ziplink/ssl handshake
* handshaketimer.h Header for above code
* link.h Contains the definition of the Link block class
* main.cpp The main group of classes and code for the module class
* main.h The header for the main file
* resolvers.h The header file that defines certain DNS utility classes
* treesocket.cpp Contains code that inherits InspSocket into a server socket
* treesocket.h Header definitions for above code
* treeserver.cpp Contains code that defines the behaviour of a single server
* treeserver.h Header definitions for above code
* utils.cpp Contains general and message routing utility classes
* utils.h Header code for general and message routing utilities
Have fun
-- Brain :-)
\ No newline at end of file +m_spanningtree +-------------- + +This directory contains all files required to build the m_spanningtree.so module. +Directories like this one starting with m_ and containing .cpp files are aggregated +together by the makefile to form a single .so, with the same name as the directory. + +This directory contains the following files: + +* handshaketimer.cpp Code for detecting end of ziplink/ssl handshake +* handshaketimer.h Header for above code +* link.h Contains the definition of the Link block class +* main.cpp The main group of classes and code for the module class +* main.h The header for the main file +* resolvers.h The header file that defines certain DNS utility classes +* treesocket.cpp Contains code that inherits InspSocket into a server socket +* treesocket.h Header definitions for above code +* treeserver.cpp Contains code that defines the behaviour of a single server +* treeserver.h Header definitions for above code +* utils.cpp Contains general and message routing utility classes +* utils.h Header code for general and message routing utilities + +Have fun + -- Brain :-) diff --git a/src/modules/m_spanningtree/handshaketimer.cpp b/src/modules/m_spanningtree/handshaketimer.cpp index 93856f467..4aeb1da88 100644 --- a/src/modules/m_spanningtree/handshaketimer.cpp +++ b/src/modules/m_spanningtree/handshaketimer.cpp @@ -1 +1,62 @@ -/* +------------------------------------+
* | 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 "users.h"
#include "channels.h"
#include "modules.h"
#include "commands/cmd_whois.h"
#include "commands/cmd_stats.h"
#include "socket.h"
#include "wildcard.h"
#include "xline.h"
#include "transport.h"
#include "m_spanningtree/main.h"
#include "m_spanningtree/utils.h"
#include "m_spanningtree/treeserver.h"
#include "m_spanningtree/link.h"
#include "m_spanningtree/treesocket.h"
#include "m_spanningtree/handshaketimer.h"
/* $ModDep: m_spanningtree/main.h m_spanningtree/utils.h m_spanningtree/treeserver.h m_spanningtree/link.h m_spanningtree/treesocket.h */
HandshakeTimer::HandshakeTimer(InspIRCd* Inst, TreeSocket* s, Link* l, SpanningTreeUtilities* u, int delay) : InspTimer(delay, time(NULL)), Instance(Inst), sock(s), lnk(l), Utils(u)
{
thefd = sock->GetFd();
}
void HandshakeTimer::Tick(time_t TIME)
{
if (Instance->SE->GetRef(thefd) == sock)
{
if (!sock->GetHook())
{
sock->SendCapabilities();
}
else
{
if (sock->GetHook() && InspSocketHSCompleteRequest(sock, (Module*)Utils->Creator, sock->GetHook()).Send())
{
InspSocketAttachCertRequest(sock, (Module*)Utils->Creator, sock->GetHook()).Send();
sock->SendCapabilities();
}
else
{
Instance->Timers->AddTimer(new HandshakeTimer(Instance, sock, lnk, Utils, 1));
}
}
}
}
\ No newline at end of file +/* +------------------------------------+ + * | 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 "users.h" +#include "channels.h" +#include "modules.h" +#include "commands/cmd_whois.h" +#include "commands/cmd_stats.h" +#include "socket.h" +#include "wildcard.h" +#include "xline.h" +#include "transport.h" + +#include "m_spanningtree/main.h" +#include "m_spanningtree/utils.h" +#include "m_spanningtree/treeserver.h" +#include "m_spanningtree/link.h" +#include "m_spanningtree/treesocket.h" +#include "m_spanningtree/handshaketimer.h" + +/* $ModDep: m_spanningtree/main.h m_spanningtree/utils.h m_spanningtree/treeserver.h m_spanningtree/link.h m_spanningtree/treesocket.h */ + +HandshakeTimer::HandshakeTimer(InspIRCd* Inst, TreeSocket* s, Link* l, SpanningTreeUtilities* u, int delay) : InspTimer(delay, time(NULL)), Instance(Inst), sock(s), lnk(l), Utils(u) +{ + thefd = sock->GetFd(); +} + +void HandshakeTimer::Tick(time_t TIME) +{ + if (Instance->SE->GetRef(thefd) == sock) + { + if (!sock->GetHook()) + { + sock->SendCapabilities(); + } + else + { + if (sock->GetHook() && InspSocketHSCompleteRequest(sock, (Module*)Utils->Creator, sock->GetHook()).Send()) + { + InspSocketAttachCertRequest(sock, (Module*)Utils->Creator, sock->GetHook()).Send(); + sock->SendCapabilities(); + } + else + { + Instance->Timers->AddTimer(new HandshakeTimer(Instance, sock, lnk, Utils, 1)); + } + } + } +} + diff --git a/src/modules/m_spanningtree/handshaketimer.h b/src/modules/m_spanningtree/handshaketimer.h index e94fe67d7..496102dda 100644 --- a/src/modules/m_spanningtree/handshaketimer.h +++ b/src/modules/m_spanningtree/handshaketimer.h @@ -1 +1,37 @@ -/* +------------------------------------+
* | 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.
*
* ---------------------------------------------------
*/
#ifndef __HANDSHAKE_TIMER_H__
#define __HANDSHAKE_TIMER_H__
#include "inspircd.h"
#include "timer.h"
class SpanningTreeUtilities;
class TreeSocket;
class Link;
class HandshakeTimer : public InspTimer
{
private:
InspIRCd* Instance;
TreeSocket* sock;
Link* lnk;
SpanningTreeUtilities* Utils;
int thefd;
public:
HandshakeTimer(InspIRCd* Inst, TreeSocket* s, Link* l, SpanningTreeUtilities* u, int delay);
virtual void Tick(time_t TIME);
};
#endif
\ No newline at end of file +/* +------------------------------------+ + * | 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. + * + * --------------------------------------------------- + */ + +#ifndef __HANDSHAKE_TIMER_H__ +#define __HANDSHAKE_TIMER_H__ + +#include "inspircd.h" +#include "timer.h" + +class SpanningTreeUtilities; +class TreeSocket; +class Link; + +class HandshakeTimer : public InspTimer +{ + private: + InspIRCd* Instance; + TreeSocket* sock; + Link* lnk; + SpanningTreeUtilities* Utils; + int thefd; + public: + HandshakeTimer(InspIRCd* Inst, TreeSocket* s, Link* l, SpanningTreeUtilities* u, int delay); + virtual void Tick(time_t TIME); +}; + +#endif diff --git a/src/modules/m_spanningtree/link.h b/src/modules/m_spanningtree/link.h index 9636d565f..3de326153 100644 --- a/src/modules/m_spanningtree/link.h +++ b/src/modules/m_spanningtree/link.h @@ -1 +1,42 @@ -/* +------------------------------------+
* | 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.
*
* ---------------------------------------------------
*/
#ifndef __LINK_H__
#define __LINK_H__
/** The Link class might as well be a struct,
* but this is C++ and we don't believe in structs (!).
* It holds the entire information of one <link>
* tag from the main config file. We maintain a list
* of them, and populate the list on rehash/load.
*/
class Link : public classbase
{
public:
irc::string Name;
std::string IPAddr;
int Port;
std::string SendPass;
std::string RecvPass;
std::string AllowMask;
unsigned long AutoConnect;
time_t NextConnectTime;
bool HiddenFromStats;
std::string FailOver;
std::string Hook;
int Timeout;
std::string Bind;
bool Hidden;
};
#endif
\ No newline at end of file +/* +------------------------------------+ + * | 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. + * + * --------------------------------------------------- + */ + +#ifndef __LINK_H__ +#define __LINK_H__ + +/** The Link class might as well be a struct, + * but this is C++ and we don't believe in structs (!). + * It holds the entire information of one <link> + * tag from the main config file. We maintain a list + * of them, and populate the list on rehash/load. + */ +class Link : public classbase +{ + public: + irc::string Name; + std::string IPAddr; + int Port; + std::string SendPass; + std::string RecvPass; + std::string AllowMask; + unsigned long AutoConnect; + time_t NextConnectTime; + bool HiddenFromStats; + std::string FailOver; + std::string Hook; + int Timeout; + std::string Bind; + bool Hidden; +}; + +#endif diff --git a/src/modules/m_spanningtree/main.cpp b/src/modules/m_spanningtree/main.cpp index 352cae870..1cc18dae6 100644 --- a/src/modules/m_spanningtree/main.cpp +++ b/src/modules/m_spanningtree/main.cpp @@ -1 +1,1392 @@ -/* +------------------------------------+
* | 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.
*
* ---------------------------------------------------
*/
/* $ModDesc: Provides a spanning tree server link protocol */
#include "inspircd.h"
#include "configreader.h"
#include "users.h"
#include "channels.h"
#include "modules.h"
#include "commands/cmd_whois.h"
#include "commands/cmd_stats.h"
#include "socket.h"
#include "wildcard.h"
#include "xline.h"
#include "transport.h"
#include "m_spanningtree/timesynctimer.h"
#include "m_spanningtree/resolvers.h"
#include "m_spanningtree/main.h"
#include "m_spanningtree/utils.h"
#include "m_spanningtree/treeserver.h"
#include "m_spanningtree/link.h"
#include "m_spanningtree/treesocket.h"
#include "m_spanningtree/rconnect.h"
#include "m_spanningtree/rsquit.h"
/* $ModDep: m_spanningtree/timesynctimer.h m_spanningtree/resolvers.h m_spanningtree/main.h m_spanningtree/utils.h m_spanningtree/treeserver.h m_spanningtree/link.h m_spanningtree/treesocket.h m_spanningtree/rconnect.h m_spanningtree/rsquit.h */
ModuleSpanningTree::ModuleSpanningTree(InspIRCd* Me)
: Module(Me), max_local(0), max_global(0)
{
ServerInstance->UseInterface("InspSocketHook");
Utils = new SpanningTreeUtilities(Me, this);
command_rconnect = new cmd_rconnect(ServerInstance, this, Utils);
ServerInstance->AddCommand(command_rconnect);
command_rsquit = new cmd_rsquit(ServerInstance, this, Utils);
ServerInstance->AddCommand(command_rsquit);
if (Utils->EnableTimeSync)
{
SyncTimer = new TimeSyncTimer(ServerInstance, this);
ServerInstance->Timers->AddTimer(SyncTimer);
}
else
SyncTimer = NULL;
RefreshTimer = new CacheRefreshTimer(ServerInstance, Utils);
ServerInstance->Timers->AddTimer(RefreshTimer);
}
void ModuleSpanningTree::ShowLinks(TreeServer* Current, userrec* user, int hops)
{
std::string Parent = Utils->TreeRoot->GetName();
if (Current->GetParent())
{
Parent = Current->GetParent()->GetName();
}
for (unsigned int q = 0; q < Current->ChildCount(); q++)
{
if ((Current->GetChild(q)->Hidden) || ((Utils->HideULines) && (ServerInstance->ULine(Current->GetChild(q)->GetName().c_str()))))
{
if (*user->oper)
{
ShowLinks(Current->GetChild(q),user,hops+1);
}
}
else
{
ShowLinks(Current->GetChild(q),user,hops+1);
}
}
/* Don't display the line if its a uline, hide ulines is on, and the user isnt an oper */
if ((Utils->HideULines) && (ServerInstance->ULine(Current->GetName().c_str())) && (!IS_OPER(user)))
return;
/* Or if the server is hidden and they're not an oper */
else if ((Current->Hidden) && (!IS_OPER(user)))
return;
user->WriteServ("364 %s %s %s :%d %s", user->nick,Current->GetName().c_str(),
(Utils->FlatLinks && (!IS_OPER(user))) ? ServerInstance->Config->ServerName : Parent.c_str(),
(Utils->FlatLinks && (!IS_OPER(user))) ? 0 : hops,
Current->GetDesc().c_str());
}
int ModuleSpanningTree::CountLocalServs()
{
return Utils->TreeRoot->ChildCount();
}
int ModuleSpanningTree::CountServs()
{
return Utils->serverlist.size();
}
void ModuleSpanningTree::HandleLinks(const char** parameters, int pcnt, userrec* user)
{
ShowLinks(Utils->TreeRoot,user,0);
user->WriteServ("365 %s * :End of /LINKS list.",user->nick);
return;
}
void ModuleSpanningTree::HandleLusers(const char** parameters, int pcnt, userrec* user)
{
unsigned int n_users = ServerInstance->UserCount();
/* Only update these when someone wants to see them, more efficient */
if ((unsigned int)ServerInstance->LocalUserCount() > max_local)
max_local = ServerInstance->LocalUserCount();
if (n_users > max_global)
max_global = n_users;
unsigned int ulined_count = 0;
unsigned int ulined_local_count = 0;
/* If ulined are hidden and we're not an oper, count the number of ulined servers hidden,
* locally and globally (locally means directly connected to us)
*/
if ((Utils->HideULines) && (!*user->oper))
{
for (server_hash::iterator q = Utils->serverlist.begin(); q != Utils->serverlist.end(); q++)
{
if (ServerInstance->ULine(q->second->GetName().c_str()))
{
ulined_count++;
if (q->second->GetParent() == Utils->TreeRoot)
ulined_local_count++;
}
}
}
user->WriteServ("251 %s :There are %d users and %d invisible on %d servers",user->nick,n_users-ServerInstance->InvisibleUserCount(),ServerInstance->InvisibleUserCount(),ulined_count ? this->CountServs() - ulined_count : this->CountServs());
if (ServerInstance->OperCount())
user->WriteServ("252 %s %d :operator(s) online",user->nick,ServerInstance->OperCount());
if (ServerInstance->UnregisteredUserCount())
user->WriteServ("253 %s %d :unknown connections",user->nick,ServerInstance->UnregisteredUserCount());
if (ServerInstance->ChannelCount())
user->WriteServ("254 %s %d :channels formed",user->nick,ServerInstance->ChannelCount());
user->WriteServ("255 %s :I have %d clients and %d servers",user->nick,ServerInstance->LocalUserCount(),ulined_local_count ? this->CountLocalServs() - ulined_local_count : this->CountLocalServs());
user->WriteServ("265 %s :Current Local Users: %d Max: %d",user->nick,ServerInstance->LocalUserCount(),max_local);
user->WriteServ("266 %s :Current Global Users: %d Max: %d",user->nick,n_users,max_global);
return;
}
std::string ModuleSpanningTree::TimeToStr(time_t secs)
{
time_t mins_up = secs / 60;
time_t hours_up = mins_up / 60;
time_t days_up = hours_up / 24;
secs = secs % 60;
mins_up = mins_up % 60;
hours_up = hours_up % 24;
return ((days_up ? (ConvToStr(days_up) + "d") : std::string(""))
+ (hours_up ? (ConvToStr(hours_up) + "h") : std::string(""))
+ (mins_up ? (ConvToStr(mins_up) + "m") : std::string(""))
+ ConvToStr(secs) + "s");
}
const std::string ModuleSpanningTree::MapOperInfo(TreeServer* Current)
{
time_t secs_up = ServerInstance->Time() - Current->age;
return (" [Up: " + TimeToStr(secs_up) + " Lag: "+ConvToStr(Current->rtt)+"s]");
}
// WARNING: NOT THREAD SAFE - DONT GET ANY SMART IDEAS.
void ModuleSpanningTree::ShowMap(TreeServer* Current, userrec* user, int depth, char matrix[128][128], float &totusers, float &totservers)
{
if (line < 128)
{
for (int t = 0; t < depth; t++)
{
matrix[line][t] = ' ';
}
// 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[128];
/* Neat and tidy default values, as we're dealing with a matrix not a simple string */
memset(text, 0, 128);
if (ServerInstance->clientlist->size() == 0) {
// If there are no users, WHO THE HELL DID THE /MAP?!?!?!
percent = 0;
}
else
{
percent = ((float)Current->GetUserCount() / (float)ServerInstance->clientlist->size()) * 100;
}
const std::string operdata = IS_OPER(user) ? MapOperInfo(Current) : "";
snprintf(text, 126, "%s %s%5d [%5.2f%%]%s", Current->GetName().c_str(), spacer, Current->GetUserCount(), percent, operdata.c_str());
totusers += Current->GetUserCount();
totservers++;
strlcpy(&matrix[line][depth],text,126);
line++;
for (unsigned int q = 0; q < Current->ChildCount(); q++)
{
if ((Current->GetChild(q)->Hidden) || ((Utils->HideULines) && (ServerInstance->ULine(Current->GetChild(q)->GetName().c_str()))))
{
if (*user->oper)
{
ShowMap(Current->GetChild(q),user,(Utils->FlatLinks && (!*user->oper)) ? depth : depth+2,matrix,totusers,totservers);
}
}
else
{
ShowMap(Current->GetChild(q),user,(Utils->FlatLinks && (!*user->oper)) ? depth : depth+2,matrix,totusers,totservers);
}
}
}
}
int ModuleSpanningTree::HandleMotd(const char** parameters, int pcnt, userrec* user)
{
if (pcnt > 0)
{
if (match(ServerInstance->Config->ServerName, parameters[0]))
return 0;
/* Remote MOTD, the server is within the 1st parameter */
std::deque<std::string> params;
params.push_back(parameters[0]);
/* Send it out remotely, generate no reply yet */
TreeServer* s = Utils->FindServerMask(parameters[0]);
if (s)
{
params[0] = s->GetName();
Utils->DoOneToOne(user->nick, "MOTD", params, s->GetName());
}
else
user->WriteServ( "402 %s %s :No such server", user->nick, parameters[0]);
return 1;
}
return 0;
}
int ModuleSpanningTree::HandleAdmin(const char** parameters, int pcnt, userrec* user)
{
if (pcnt > 0)
{
if (match(ServerInstance->Config->ServerName, parameters[0]))
return 0;
/* Remote ADMIN, the server is within the 1st parameter */
std::deque<std::string> params;
params.push_back(parameters[0]);
/* Send it out remotely, generate no reply yet */
TreeServer* s = Utils->FindServerMask(parameters[0]);
if (s)
{
params[0] = s->GetName();
Utils->DoOneToOne(user->nick, "ADMIN", params, s->GetName());
}
else
user->WriteServ( "402 %s %s :No such server", user->nick, parameters[0]);
return 1;
}
return 0;
}
int ModuleSpanningTree::HandleModules(const char** parameters, int pcnt, userrec* user)
{
if (pcnt > 0)
{
if (match(ServerInstance->Config->ServerName, parameters[0]))
return 0;
std::deque<std::string> params;
params.push_back(parameters[0]);
TreeServer* s = Utils->FindServerMask(parameters[0]);
if (s)
{
params[0] = s->GetName();
Utils->DoOneToOne(user->nick, "MODULES", params, s->GetName());
}
else
user->WriteServ( "402 %s %s :No such server", user->nick, parameters[0]);
return 1;
}
return 0;
}
int ModuleSpanningTree::HandleStats(const char** parameters, int pcnt, userrec* user)
{
if (pcnt > 1)
{
if (match(ServerInstance->Config->ServerName, parameters[1]))
return 0;
/* Remote STATS, the server is within the 2nd parameter */
std::deque<std::string> params;
params.push_back(parameters[0]);
params.push_back(parameters[1]);
/* Send it out remotely, generate no reply yet */
TreeServer* s = Utils->FindServerMask(parameters[1]);
if (s)
{
params[1] = s->GetName();
Utils->DoOneToOne(user->nick, "STATS", params, s->GetName());
}
else
{
user->WriteServ( "402 %s %s :No such server", user->nick, parameters[1]);
}
return 1;
}
return 0;
}
// Ok, prepare to be confused.
// After much mulling over how to approach this, it struck me that
// the 'usual' way of doing a /MAP isnt the best way. Instead of
// keeping track of a ton of ascii characters, and line by line
// under recursion working out where to place them using multiplications
// and divisons, we instead render the map onto a backplane of characters
// (a character matrix), then draw the branches as a series of "L" shapes
// from the nodes. This is not only friendlier on CPU it uses less stack.
void ModuleSpanningTree::HandleMap(const char** parameters, int pcnt, userrec* user)
{
// This array represents a virtual screen which we will
// "scratch" draw to, as the console device of an irc
// client does not provide for a proper terminal.
float totusers = 0;
float totservers = 0;
char matrix[128][128];
for (unsigned int t = 0; t < 128; t++)
{
matrix[t][0] = '\0';
}
line = 0;
// The only recursive bit is called here.
ShowMap(Utils->TreeRoot,user,0,matrix,totusers,totservers);
// Process each line one by one. The algorithm has a limit of
// 128 servers (which is far more than a spanning tree should have
// anyway, so we're ok). This limit can be raised simply by making
// the character matrix deeper, 128 rows taking 10k of memory.
for (int l = 1; l < line; l++)
{
// scan across the line looking for the start of the
// servername (the recursive part of the algorithm has placed
// the servers at indented positions depending on what they
// are related to)
int first_nonspace = 0;
while (matrix[l][first_nonspace] == ' ')
{
first_nonspace++;
}
first_nonspace--;
// Draw the `- (corner) section: this may be overwritten by
// another L shape passing along the same vertical pane, becoming
// a |- (branch) section instead.
matrix[l][first_nonspace] = '-';
matrix[l][first_nonspace-1] = '`';
int l2 = l - 1;
// Draw upwards until we hit the parent server, causing possibly
// other corners (`-) to become branches (|-)
while ((matrix[l2][first_nonspace-1] == ' ') || (matrix[l2][first_nonspace-1] == '`'))
{
matrix[l2][first_nonspace-1] = '|';
l2--;
}
}
// dump the whole lot to the user. This is the easy bit, honest.
for (int t = 0; t < line; t++)
{
user->WriteServ("006 %s :%s",user->nick,&matrix[t][0]);
}
float avg_users = totusers / totservers;
user->WriteServ("270 %s :%.0f server%s and %.0f user%s, average %.2f users per server",user->nick,totservers,(totservers > 1 ? "s" : ""),totusers,(totusers > 1 ? "s" : ""),avg_users);
user->WriteServ("007 %s :End of /MAP",user->nick);
return;
}
int ModuleSpanningTree::HandleSquit(const char** parameters, int pcnt, userrec* user)
{
TreeServer* s = Utils->FindServerMask(parameters[0]);
if (s)
{
if (s == Utils->TreeRoot)
{
user->WriteServ("NOTICE %s :*** SQUIT: Foolish mortal, you cannot make a server SQUIT itself! (%s matches local server name)",user->nick,parameters[0]);
return 1;
}
TreeSocket* sock = s->GetSocket();
if (sock)
{
ServerInstance->SNO->WriteToSnoMask('l',"SQUIT: Server \002%s\002 removed from network by %s",parameters[0],user->nick);
sock->Squit(s,std::string("Server quit by ") + user->GetFullRealHost());
ServerInstance->SE->DelFd(sock);
sock->Close();
}
else
{
if (IS_LOCAL(user))
user->WriteServ("NOTICE %s :*** WARNING: Using SQUIT to split remote servers is deprecated. Please use RSQUIT instead.",user->nick);
}
}
else
{
user->WriteServ("NOTICE %s :*** SQUIT: The server \002%s\002 does not exist on the network.",user->nick,parameters[0]);
}
return 1;
}
int ModuleSpanningTree::HandleTime(const char** parameters, int pcnt, userrec* user)
{
if ((IS_LOCAL(user)) && (pcnt))
{
TreeServer* found = Utils->FindServerMask(parameters[0]);
if (found)
{
// we dont' override for local server
if (found == Utils->TreeRoot)
return 0;
std::deque<std::string> params;
params.push_back(found->GetName());
params.push_back(user->nick);
Utils->DoOneToOne(ServerInstance->Config->ServerName,"TIME",params,found->GetName());
}
else
{
user->WriteServ("402 %s %s :No such server",user->nick,parameters[0]);
}
}
return 1;
}
int ModuleSpanningTree::HandleRemoteWhois(const char** parameters, int pcnt, userrec* user)
{
if ((IS_LOCAL(user)) && (pcnt > 1))
{
userrec* remote = ServerInstance->FindNick(parameters[1]);
if ((remote) && (remote->GetFd() < 0))
{
std::deque<std::string> params;
params.push_back(parameters[1]);
Utils->DoOneToOne(user->nick,"IDLE",params,remote->server);
return 1;
}
else if (!remote)
{
user->WriteServ("401 %s %s :No such nick/channel",user->nick, parameters[1]);
user->WriteServ("318 %s %s :End of /WHOIS list.",user->nick, parameters[1]);
return 1;
}
}
return 0;
}
void ModuleSpanningTree::DoPingChecks(time_t curtime)
{
for (unsigned int j = 0; j < Utils->TreeRoot->ChildCount(); j++)
{
TreeServer* serv = Utils->TreeRoot->GetChild(j);
TreeSocket* sock = serv->GetSocket();
if (sock)
{
if (curtime >= serv->NextPingTime())
{
if (serv->AnsweredLastPing())
{
sock->WriteLine(std::string(":")+ServerInstance->Config->ServerName+" PING "+serv->GetName());
serv->SetNextPingTime(curtime + 60);
serv->LastPing = curtime;
serv->Warned = false;
}
else
{
/* they didnt answer, boot them */
sock->SendError("Ping timeout");
sock->Squit(serv,"Ping timeout");
/*** XXX SOCKET CULL ***/
return;
}
}
else if ((Utils->PingWarnTime) && (!serv->Warned) && (curtime >= serv->NextPingTime() - (60 - Utils->PingWarnTime)) && (!serv->AnsweredLastPing()))
{
/* The server hasnt responded, send a warning to opers */
ServerInstance->SNO->WriteToSnoMask('l',"Server \002%s\002 has not responded to PING for %d seconds, high latency.", serv->GetName().c_str(), Utils->PingWarnTime);
serv->Warned = true;
}
}
}
/* Cancel remote burst mode on any servers which still have it enabled due to latency/lack of data.
* This prevents lost REMOTECONNECT notices
*/
for (server_hash::iterator i = Utils->serverlist.begin(); i != Utils->serverlist.end(); i++)
Utils->SetRemoteBursting(i->second, false);
}
void ModuleSpanningTree::ConnectServer(Link* x)
{
bool ipvalid = true;
QueryType start_type = DNS_QUERY_A;
#ifdef IPV6
start_type = DNS_QUERY_AAAA;
if (strchr(x->IPAddr.c_str(),':'))
{
in6_addr n;
if (inet_pton(AF_INET6, x->IPAddr.c_str(), &n) < 1)
ipvalid = false;
}
else
#endif
{
in_addr n;
if (inet_aton(x->IPAddr.c_str(),&n) < 1)
ipvalid = false;
}
/* Do we already have an IP? If so, no need to resolve it. */
if (ipvalid)
{
/* Gave a hook, but it wasnt one we know */
if ((!x->Hook.empty()) && (Utils->hooks.find(x->Hook.c_str()) == Utils->hooks.end()))
return;
TreeSocket* newsocket = new TreeSocket(Utils, ServerInstance, x->IPAddr,x->Port,false,x->Timeout ? x->Timeout : 10,x->Name.c_str(), x->Bind, x->Hook.empty() ? NULL : Utils->hooks[x->Hook.c_str()]);
if (newsocket->GetFd() > -1)
{
/* Handled automatically on success */
}
else
{
ServerInstance->SNO->WriteToSnoMask('l',"CONNECT: Error connecting \002%s\002: %s.",x->Name.c_str(),strerror(errno));
delete newsocket;
Utils->DoFailOver(x);
}
}
else
{
try
{
bool cached;
ServernameResolver* snr = new ServernameResolver((Module*)this, Utils, ServerInstance,x->IPAddr, *x, cached, start_type);
ServerInstance->AddResolver(snr, cached);
}
catch (ModuleException& e)
{
ServerInstance->SNO->WriteToSnoMask('l',"CONNECT: Error connecting \002%s\002: %s.",x->Name.c_str(), e.GetReason());
Utils->DoFailOver(x);
}
}
}
void ModuleSpanningTree::AutoConnectServers(time_t curtime)
{
for (std::vector<Link>::iterator x = Utils->LinkBlocks.begin(); x < Utils->LinkBlocks.end(); x++)
{
if ((x->AutoConnect) && (curtime >= x->NextConnectTime))
{
x->NextConnectTime = curtime + x->AutoConnect;
TreeServer* CheckDupe = Utils->FindServer(x->Name.c_str());
if (x->FailOver.length())
{
TreeServer* CheckFailOver = Utils->FindServer(x->FailOver.c_str());
if (CheckFailOver)
{
/* The failover for this server is currently a member of the network.
* The failover probably succeeded, where the main link did not.
* Don't try the main link until the failover is gone again.
*/
continue;
}
}
if (!CheckDupe)
{
// an autoconnected server is not connected. Check if its time to connect it
ServerInstance->SNO->WriteToSnoMask('l',"AUTOCONNECT: Auto-connecting server \002%s\002 (%lu seconds until next attempt)",x->Name.c_str(),x->AutoConnect);
this->ConnectServer(&(*x));
}
}
}
}
int ModuleSpanningTree::HandleVersion(const char** parameters, int pcnt, userrec* user)
{
// we've already checked if pcnt > 0, so this is safe
TreeServer* found = Utils->FindServerMask(parameters[0]);
if (found)
{
std::string Version = found->GetVersion();
user->WriteServ("351 %s :%s",user->nick,Version.c_str());
if (found == Utils->TreeRoot)
{
ServerInstance->Config->Send005(user);
}
}
else
{
user->WriteServ("402 %s %s :No such server",user->nick,parameters[0]);
}
return 1;
}
int ModuleSpanningTree::HandleConnect(const char** parameters, int pcnt, userrec* user)
{
for (std::vector<Link>::iterator x = Utils->LinkBlocks.begin(); x < Utils->LinkBlocks.end(); x++)
{
if (ServerInstance->MatchText(x->Name.c_str(),parameters[0]))
{
TreeServer* CheckDupe = Utils->FindServer(x->Name.c_str());
if (!CheckDupe)
{
user->WriteServ("NOTICE %s :*** CONNECT: Connecting to server: \002%s\002 (%s:%d)",user->nick,x->Name.c_str(),(x->HiddenFromStats ? "<hidden>" : x->IPAddr.c_str()),x->Port);
ConnectServer(&(*x));
return 1;
}
else
{
user->WriteServ("NOTICE %s :*** CONNECT: Server \002%s\002 already exists on the network and is connected via \002%s\002",user->nick,x->Name.c_str(),CheckDupe->GetParent()->GetName().c_str());
return 1;
}
}
}
user->WriteServ("NOTICE %s :*** CONNECT: No server matching \002%s\002 could be found in the config file.",user->nick,parameters[0]);
return 1;
}
void ModuleSpanningTree::BroadcastTimeSync()
{
if (Utils->MasterTime)
{
std::deque<std::string> params;
params.push_back(ConvToStr(ServerInstance->Time(false)));
params.push_back("FORCE");
Utils->DoOneToMany(Utils->TreeRoot->GetName(), "TIMESET", params);
}
}
int ModuleSpanningTree::OnStats(char statschar, userrec* user, string_list &results)
{
if ((statschar == 'c') || (statschar == 'n'))
{
for (unsigned int i = 0; i < Utils->LinkBlocks.size(); i++)
{
results.push_back(std::string(ServerInstance->Config->ServerName)+" 213 "+user->nick+" "+statschar+" *@"+(Utils->LinkBlocks[i].HiddenFromStats ? "<hidden>" : Utils->LinkBlocks[i].IPAddr)+" * "+Utils->LinkBlocks[i].Name.c_str()+" "+ConvToStr(Utils->LinkBlocks[i].Port)+" "+(Utils->LinkBlocks[i].Hook.empty() ? "plaintext" : Utils->LinkBlocks[i].Hook)+" "+(Utils->LinkBlocks[i].AutoConnect ? 'a' : '-')+'s');
if (statschar == 'c')
results.push_back(std::string(ServerInstance->Config->ServerName)+" 244 "+user->nick+" H * * "+Utils->LinkBlocks[i].Name.c_str());
}
results.push_back(std::string(ServerInstance->Config->ServerName)+" 219 "+user->nick+" "+statschar+" :End of /STATS report");
ServerInstance->SNO->WriteToSnoMask('t',"%s '%c' requested by %s (%s@%s)",(!strcmp(user->server,ServerInstance->Config->ServerName) ? "Stats" : "Remote stats"),statschar,user->nick,user->ident,user->host);
return 1;
}
if (statschar == 'p')
{
/* show all server ports, after showing client ports. -- w00t */
for (unsigned int i = 0; i < Utils->Bindings.size(); i++)
{
std::string ip = Utils->Bindings[i]->IP;
if (ip.empty())
ip = "*";
std::string transport("plaintext");
if (Utils->Bindings[i]->GetHook())
transport = InspSocketNameRequest(this, Utils->Bindings[i]->GetHook()).Send();
results.push_back(ConvToStr(ServerInstance->Config->ServerName) + " 249 "+user->nick+" :" + ip + ":" + ConvToStr(Utils->Bindings[i]->port)+
" (server, " + transport + ")");
}
}
return 0;
}
int ModuleSpanningTree::OnPreCommand(const std::string &command, const char** parameters, int pcnt, userrec *user, bool validated, const std::string &original_line)
{
/* If the command doesnt appear to be valid, we dont want to mess with it. */
if (!validated)
return 0;
if (command == "CONNECT")
{
return this->HandleConnect(parameters,pcnt,user);
}
else if (command == "STATS")
{
return this->HandleStats(parameters,pcnt,user);
}
else if (command == "MOTD")
{
return this->HandleMotd(parameters,pcnt,user);
}
else if (command == "ADMIN")
{
return this->HandleAdmin(parameters,pcnt,user);
}
else if (command == "SQUIT")
{
return this->HandleSquit(parameters,pcnt,user);
}
else if (command == "MAP")
{
this->HandleMap(parameters,pcnt,user);
return 1;
}
else if ((command == "TIME") && (pcnt > 0))
{
return this->HandleTime(parameters,pcnt,user);
}
else if (command == "LUSERS")
{
this->HandleLusers(parameters,pcnt,user);
return 1;
}
else if (command == "LINKS")
{
this->HandleLinks(parameters,pcnt,user);
return 1;
}
else if (command == "WHOIS")
{
if (pcnt > 1)
{
// remote whois
return this->HandleRemoteWhois(parameters,pcnt,user);
}
}
else if ((command == "VERSION") && (pcnt > 0))
{
this->HandleVersion(parameters,pcnt,user);
return 1;
}
else if ((command == "MODULES") && (pcnt > 0))
{
return this->HandleModules(parameters,pcnt,user);
}
return 0;
}
void ModuleSpanningTree::OnPostCommand(const std::string &command, const char** parameters, int pcnt, userrec *user, CmdResult result, const std::string &original_line)
{
if ((result == CMD_SUCCESS) && (ServerInstance->IsValidModuleCommand(command, pcnt, user)))
{
// this bit of code cleverly routes all module commands
// to all remote severs *automatically* so that modules
// can just handle commands locally, without having
// to have any special provision in place for remote
// commands and linking protocols.
std::deque<std::string> params;
params.clear();
for (int j = 0; j < pcnt; j++)
{
if (strchr(parameters[j],' '))
{
params.push_back(":" + std::string(parameters[j]));
}
else
{
params.push_back(std::string(parameters[j]));
}
}
Utils->DoOneToMany(user->nick,command,params);
}
}
void ModuleSpanningTree::OnGetServerDescription(const std::string &servername,std::string &description)
{
TreeServer* s = Utils->FindServer(servername);
if (s)
{
description = s->GetDesc();
}
}
void ModuleSpanningTree::OnUserInvite(userrec* source,userrec* dest,chanrec* channel)
{
if (IS_LOCAL(source))
{
std::deque<std::string> params;
params.push_back(dest->nick);
params.push_back(channel->name);
Utils->DoOneToMany(source->nick,"INVITE",params);
}
}
void ModuleSpanningTree::OnPostLocalTopicChange(userrec* user, chanrec* chan, const std::string &topic)
{
std::deque<std::string> params;
params.push_back(chan->name);
params.push_back(":"+topic);
Utils->DoOneToMany(user->nick,"TOPIC",params);
}
void ModuleSpanningTree::OnWallops(userrec* user, const std::string &text)
{
if (IS_LOCAL(user))
{
std::deque<std::string> params;
params.push_back(":"+text);
Utils->DoOneToMany(user->nick,"WALLOPS",params);
}
}
void ModuleSpanningTree::OnUserNotice(userrec* user, void* dest, int target_type, const std::string &text, char status, const CUList &exempt_list)
{
if (target_type == TYPE_USER)
{
userrec* d = (userrec*)dest;
if ((d->GetFd() < 0) && (IS_LOCAL(user)))
{
std::deque<std::string> params;
params.clear();
params.push_back(d->nick);
params.push_back(":"+text);
Utils->DoOneToOne(user->nick,"NOTICE",params,d->server);
}
}
else if (target_type == TYPE_CHANNEL)
{
if (IS_LOCAL(user))
{
chanrec *c = (chanrec*)dest;
if (c)
{
std::string cname = c->name;
if (status)
cname = status + cname;
TreeServerList list;
Utils->GetListOfServersForChannel(c,list,status,exempt_list);
for (TreeServerList::iterator i = list.begin(); i != list.end(); i++)
{
TreeSocket* Sock = i->second->GetSocket();
if (Sock)
Sock->WriteLine(":"+std::string(user->nick)+" NOTICE "+cname+" :"+text);
}
}
}
}
else if (target_type == TYPE_SERVER)
{
if (IS_LOCAL(user))
{
char* target = (char*)dest;
std::deque<std::string> par;
par.push_back(target);
par.push_back(":"+text);
Utils->DoOneToMany(user->nick,"NOTICE",par);
}
}
}
void ModuleSpanningTree::OnUserMessage(userrec* user, void* dest, int target_type, const std::string &text, char status, const CUList &exempt_list)
{
if (target_type == TYPE_USER)
{
// route private messages which are targetted at clients only to the server
// which needs to receive them
userrec* d = (userrec*)dest;
if ((d->GetFd() < 0) && (IS_LOCAL(user)))
{
std::deque<std::string> params;
params.clear();
params.push_back(d->nick);
params.push_back(":"+text);
Utils->DoOneToOne(user->nick,"PRIVMSG",params,d->server);
}
}
else if (target_type == TYPE_CHANNEL)
{
if (IS_LOCAL(user))
{
chanrec *c = (chanrec*)dest;
if (c)
{
std::string cname = c->name;
if (status)
cname = status + cname;
TreeServerList list;
Utils->GetListOfServersForChannel(c,list,status,exempt_list);
for (TreeServerList::iterator i = list.begin(); i != list.end(); i++)
{
TreeSocket* Sock = i->second->GetSocket();
if (Sock)
Sock->WriteLine(":"+std::string(user->nick)+" PRIVMSG "+cname+" :"+text);
}
}
}
}
else if (target_type == TYPE_SERVER)
{
if (IS_LOCAL(user))
{
char* target = (char*)dest;
std::deque<std::string> par;
par.push_back(target);
par.push_back(":"+text);
Utils->DoOneToMany(user->nick,"PRIVMSG",par);
}
}
}
void ModuleSpanningTree::OnBackgroundTimer(time_t curtime)
{
AutoConnectServers(curtime);
DoPingChecks(curtime);
}
void ModuleSpanningTree::OnUserJoin(userrec* user, chanrec* channel, bool &silent)
{
// Only do this for local users
if (IS_LOCAL(user))
{
if (channel->GetUserCounter() == 1)
{
std::deque<std::string> params;
// set up their permissions and the channel TS with FJOIN.
// All users are FJOINed now, because a module may specify
// new joining permissions for the user.
params.push_back(channel->name);
params.push_back(ConvToStr(channel->age));
params.push_back(std::string(channel->GetAllPrefixChars(user))+","+std::string(user->nick));
Utils->DoOneToMany(ServerInstance->Config->ServerName,"FJOIN",params);
/* First user in, sync the modes for the channel */
params.pop_back();
params.push_back(channel->ChanModes(true));
Utils->DoOneToMany(ServerInstance->Config->ServerName,"FMODE",params);
}
else
{
std::deque<std::string> params;
params.push_back(channel->name);
params.push_back(ConvToStr(channel->age));
Utils->DoOneToMany(user->nick,"JOIN",params);
}
}
}
void ModuleSpanningTree::OnChangeHost(userrec* user, const std::string &newhost)
{
// only occurs for local clients
if (user->registered != REG_ALL)
return;
std::deque<std::string> params;
params.push_back(newhost);
Utils->DoOneToMany(user->nick,"FHOST",params);
}
void ModuleSpanningTree::OnChangeName(userrec* user, const std::string &gecos)
{
// only occurs for local clients
if (user->registered != REG_ALL)
return;
std::deque<std::string> params;
params.push_back(gecos);
Utils->DoOneToMany(user->nick,"FNAME",params);
}
void ModuleSpanningTree::OnUserPart(userrec* user, chanrec* channel, const std::string &partmessage, bool &silent)
{
if (IS_LOCAL(user))
{
std::deque<std::string> params;
params.push_back(channel->name);
if (!partmessage.empty())
params.push_back(":"+partmessage);
Utils->DoOneToMany(user->nick,"PART",params);
}
}
void ModuleSpanningTree::OnUserConnect(userrec* user)
{
char agestr[MAXBUF];
if (IS_LOCAL(user))
{
std::deque<std::string> params;
snprintf(agestr,MAXBUF,"%lu",(unsigned long)user->age);
params.push_back(agestr);
params.push_back(user->nick);
params.push_back(user->host);
params.push_back(user->dhost);
params.push_back(user->ident);
params.push_back("+"+std::string(user->FormatModes()));
params.push_back(user->GetIPString());
params.push_back(":"+std::string(user->fullname));
Utils->DoOneToMany(ServerInstance->Config->ServerName,"NICK",params);
// User is Local, change needs to be reflected!
TreeServer* SourceServer = Utils->FindServer(user->server);
if (SourceServer)
{
SourceServer->AddUserCount();
}
}
}
void ModuleSpanningTree::OnUserQuit(userrec* user, const std::string &reason, const std::string &oper_message)
{
if ((IS_LOCAL(user)) && (user->registered == REG_ALL))
{
std::deque<std::string> params;
if (oper_message != reason)
{
params.push_back(":"+oper_message);
Utils->DoOneToMany(user->nick,"OPERQUIT",params);
}
params.clear();
params.push_back(":"+reason);
Utils->DoOneToMany(user->nick,"QUIT",params);
}
// Regardless, We need to modify the user Counts..
TreeServer* SourceServer = Utils->FindServer(user->server);
if (SourceServer)
{
SourceServer->DelUserCount();
}
}
void ModuleSpanningTree::OnUserPostNick(userrec* user, const std::string &oldnick)
{
if (IS_LOCAL(user))
{
std::deque<std::string> params;
params.push_back(user->nick);
Utils->DoOneToMany(oldnick,"NICK",params);
}
}
void ModuleSpanningTree::OnUserKick(userrec* source, userrec* user, chanrec* chan, const std::string &reason, bool &silent)
{
if ((source) && (IS_LOCAL(source)))
{
std::deque<std::string> params;
params.push_back(chan->name);
params.push_back(user->nick);
params.push_back(":"+reason);
Utils->DoOneToMany(source->nick,"KICK",params);
}
else if (!source)
{
std::deque<std::string> params;
params.push_back(chan->name);
params.push_back(user->nick);
params.push_back(":"+reason);
Utils->DoOneToMany(ServerInstance->Config->ServerName,"KICK",params);
}
}
void ModuleSpanningTree::OnRemoteKill(userrec* source, userrec* dest, const std::string &reason, const std::string &operreason)
{
std::deque<std::string> params;
params.push_back(":"+reason);
Utils->DoOneToMany(dest->nick,"OPERQUIT",params);
params.clear();
params.push_back(dest->nick);
params.push_back(":"+reason);
dest->SetOperQuit(operreason);
Utils->DoOneToMany(source->nick,"KILL",params);
}
void ModuleSpanningTree::OnRehash(userrec* user, const std::string ¶meter)
{
if (!parameter.empty())
{
std::deque<std::string> params;
params.push_back(parameter);
Utils->DoOneToMany(user ? user->nick : ServerInstance->Config->ServerName, "REHASH", params);
// check for self
if (ServerInstance->MatchText(ServerInstance->Config->ServerName,parameter))
{
ServerInstance->WriteOpers("*** Remote rehash initiated locally by \002%s\002", user ? user->nick : ServerInstance->Config->ServerName);
ServerInstance->RehashServer();
}
}
Utils->ReadConfiguration(false);
InitializeDisabledCommands(ServerInstance->Config->DisabledCommands, ServerInstance);
}
// note: the protocol does not allow direct umode +o except
// via NICK with 8 params. sending OPERTYPE infers +o modechange
// locally.
void ModuleSpanningTree::OnOper(userrec* user, const std::string &opertype)
{
if (IS_LOCAL(user))
{
std::deque<std::string> params;
params.push_back(opertype);
Utils->DoOneToMany(user->nick,"OPERTYPE",params);
}
}
void ModuleSpanningTree::OnLine(userrec* source, const std::string &host, bool adding, char linetype, long duration, const std::string &reason)
{
if (!source)
{
/* Server-set lines */
char data[MAXBUF];
snprintf(data,MAXBUF,"%c %s %s %lu %lu :%s", linetype, host.c_str(), ServerInstance->Config->ServerName, (unsigned long)ServerInstance->Time(false),
(unsigned long)duration, reason.c_str());
std::deque<std::string> params;
params.push_back(data);
Utils->DoOneToMany(ServerInstance->Config->ServerName, "ADDLINE", params);
}
else
{
if (IS_LOCAL(source))
{
char type[8];
snprintf(type,8,"%cLINE",linetype);
std::string stype = type;
if (adding)
{
char sduration[MAXBUF];
snprintf(sduration,MAXBUF,"%ld",duration);
std::deque<std::string> params;
params.push_back(host);
params.push_back(sduration);
params.push_back(":"+reason);
Utils->DoOneToMany(source->nick,stype,params);
}
else
{
std::deque<std::string> params;
params.push_back(host);
Utils->DoOneToMany(source->nick,stype,params);
}
}
}
}
void ModuleSpanningTree::OnAddGLine(long duration, userrec* source, const std::string &reason, const std::string &hostmask)
{
OnLine(source,hostmask,true,'G',duration,reason);
}
void ModuleSpanningTree::OnAddZLine(long duration, userrec* source, const std::string &reason, const std::string &ipmask)
{
OnLine(source,ipmask,true,'Z',duration,reason);
}
void ModuleSpanningTree::OnAddQLine(long duration, userrec* source, const std::string &reason, const std::string &nickmask)
{
OnLine(source,nickmask,true,'Q',duration,reason);
}
void ModuleSpanningTree::OnAddELine(long duration, userrec* source, const std::string &reason, const std::string &hostmask)
{
OnLine(source,hostmask,true,'E',duration,reason);
}
void ModuleSpanningTree::OnDelGLine(userrec* source, const std::string &hostmask)
{
OnLine(source,hostmask,false,'G',0,"");
}
void ModuleSpanningTree::OnDelZLine(userrec* source, const std::string &ipmask)
{
OnLine(source,ipmask,false,'Z',0,"");
}
void ModuleSpanningTree::OnDelQLine(userrec* source, const std::string &nickmask)
{
OnLine(source,nickmask,false,'Q',0,"");
}
void ModuleSpanningTree::OnDelELine(userrec* source, const std::string &hostmask)
{
OnLine(source,hostmask,false,'E',0,"");
}
void ModuleSpanningTree::OnMode(userrec* user, void* dest, int target_type, const std::string &text)
{
if ((IS_LOCAL(user)) && (user->registered == REG_ALL))
{
std::deque<std::string> params;
std::string command;
if (target_type == TYPE_USER)
{
userrec* u = (userrec*)dest;
params.push_back(u->nick);
params.push_back(text);
command = "MODE";
}
else
{
chanrec* c = (chanrec*)dest;
params.push_back(c->name);
params.push_back(ConvToStr(c->age));
params.push_back(text);
command = "FMODE";
}
Utils->DoOneToMany(user->nick, command, params);
}
}
void ModuleSpanningTree::OnSetAway(userrec* user)
{
if (IS_LOCAL(user))
{
std::deque<std::string> params;
params.push_back(":"+std::string(user->awaymsg));
Utils->DoOneToMany(user->nick,"AWAY",params);
}
}
void ModuleSpanningTree::OnCancelAway(userrec* user)
{
if (IS_LOCAL(user))
{
std::deque<std::string> params;
params.clear();
Utils->DoOneToMany(user->nick,"AWAY",params);
}
}
void ModuleSpanningTree::ProtoSendMode(void* opaque, int target_type, void* target, const std::string &modeline)
{
TreeSocket* s = (TreeSocket*)opaque;
if (target)
{
if (target_type == TYPE_USER)
{
userrec* u = (userrec*)target;
s->WriteLine(std::string(":")+ServerInstance->Config->ServerName+" FMODE "+u->nick+" "+ConvToStr(u->age)+" "+modeline);
}
else
{
chanrec* c = (chanrec*)target;
s->WriteLine(std::string(":")+ServerInstance->Config->ServerName+" FMODE "+c->name+" "+ConvToStr(c->age)+" "+modeline);
}
}
}
void ModuleSpanningTree::ProtoSendMetaData(void* opaque, int target_type, void* target, const std::string &extname, const std::string &extdata)
{
TreeSocket* s = (TreeSocket*)opaque;
if (target)
{
if (target_type == TYPE_USER)
{
userrec* u = (userrec*)target;
s->WriteLine(std::string(":")+ServerInstance->Config->ServerName+" METADATA "+u->nick+" "+extname+" :"+extdata);
}
else if (target_type == TYPE_CHANNEL)
{
chanrec* c = (chanrec*)target;
s->WriteLine(std::string(":")+ServerInstance->Config->ServerName+" METADATA "+c->name+" "+extname+" :"+extdata);
}
}
if (target_type == TYPE_OTHER)
{
s->WriteLine(std::string(":")+ServerInstance->Config->ServerName+" METADATA * "+extname+" :"+extdata);
}
}
void ModuleSpanningTree::OnEvent(Event* event)
{
std::deque<std::string>* params = (std::deque<std::string>*)event->GetData();
if (event->GetEventID() == "send_metadata")
{
if (params->size() < 3)
return;
(*params)[2] = ":" + (*params)[2];
Utils->DoOneToMany(ServerInstance->Config->ServerName,"METADATA",*params);
}
else if (event->GetEventID() == "send_topic")
{
if (params->size() < 2)
return;
(*params)[1] = ":" + (*params)[1];
params->insert(params->begin() + 1,ServerInstance->Config->ServerName);
params->insert(params->begin() + 1,ConvToStr(ServerInstance->Time(true)));
Utils->DoOneToMany(ServerInstance->Config->ServerName,"FTOPIC",*params);
}
else if (event->GetEventID() == "send_mode")
{
if (params->size() < 2)
return;
// Insert the TS value of the object, either userrec or chanrec
time_t ourTS = 0;
userrec* a = ServerInstance->FindNick((*params)[0]);
if (a)
{
ourTS = a->age;
Utils->DoOneToMany(ServerInstance->Config->ServerName,"MODE",*params);
return;
}
else
{
chanrec* a = ServerInstance->FindChan((*params)[0]);
if (a)
{
ourTS = a->age;
params->insert(params->begin() + 1,ConvToStr(ourTS));
Utils->DoOneToMany(ServerInstance->Config->ServerName,"FMODE",*params);
}
}
}
else if (event->GetEventID() == "send_mode_explicit")
{
if (params->size() < 2)
return;
Utils->DoOneToMany(ServerInstance->Config->ServerName,"MODE",*params);
}
else if (event->GetEventID() == "send_opers")
{
if (params->size() < 1)
return;
(*params)[0] = ":" + (*params)[0];
Utils->DoOneToMany(ServerInstance->Config->ServerName,"OPERNOTICE",*params);
}
else if (event->GetEventID() == "send_modeset")
{
if (params->size() < 2)
return;
(*params)[1] = ":" + (*params)[1];
Utils->DoOneToMany(ServerInstance->Config->ServerName,"MODENOTICE",*params);
}
else if (event->GetEventID() == "send_snoset")
{
if (params->size() < 2)
return;
(*params)[1] = ":" + (*params)[1];
Utils->DoOneToMany(ServerInstance->Config->ServerName,"SNONOTICE",*params);
}
else if (event->GetEventID() == "send_push")
{
if (params->size() < 2)
return;
userrec *a = ServerInstance->FindNick((*params)[0]);
if (!a)
return;
(*params)[1] = ":" + (*params)[1];
Utils->DoOneToOne(ServerInstance->Config->ServerName, "PUSH", *params, a->server);
}
}
ModuleSpanningTree::~ModuleSpanningTree()
{
/* This will also free the listeners */
delete Utils;
if (SyncTimer)
ServerInstance->Timers->DelTimer(SyncTimer);
ServerInstance->Timers->DelTimer(RefreshTimer);
ServerInstance->DoneWithInterface("InspSocketHook");
}
Version ModuleSpanningTree::GetVersion()
{
return Version(1,1,0,2,VF_VENDOR,API_VERSION);
}
void ModuleSpanningTree::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] = List[I_OnEvent] = List[I_OnSetAway] = List[I_OnCancelAway] = List[I_OnPostCommand] = 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 ModuleSpanningTree::Prioritize()
{
return PRIORITY_LAST;
}
MODULE_INIT(ModuleSpanningTree)
\ No newline at end of file +/* +------------------------------------+ + * | 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. + * + * --------------------------------------------------- + */ + +/* $ModDesc: Provides a spanning tree server link protocol */ + +#include "inspircd.h" +#include "configreader.h" +#include "users.h" +#include "channels.h" +#include "modules.h" +#include "commands/cmd_whois.h" +#include "commands/cmd_stats.h" +#include "socket.h" +#include "wildcard.h" +#include "xline.h" +#include "transport.h" + +#include "m_spanningtree/timesynctimer.h" +#include "m_spanningtree/resolvers.h" +#include "m_spanningtree/main.h" +#include "m_spanningtree/utils.h" +#include "m_spanningtree/treeserver.h" +#include "m_spanningtree/link.h" +#include "m_spanningtree/treesocket.h" +#include "m_spanningtree/rconnect.h" +#include "m_spanningtree/rsquit.h" + +/* $ModDep: m_spanningtree/timesynctimer.h m_spanningtree/resolvers.h m_spanningtree/main.h m_spanningtree/utils.h m_spanningtree/treeserver.h m_spanningtree/link.h m_spanningtree/treesocket.h m_spanningtree/rconnect.h m_spanningtree/rsquit.h */ + +ModuleSpanningTree::ModuleSpanningTree(InspIRCd* Me) + : Module(Me), max_local(0), max_global(0) +{ + ServerInstance->UseInterface("InspSocketHook"); + Utils = new SpanningTreeUtilities(Me, this); + command_rconnect = new cmd_rconnect(ServerInstance, this, Utils); + ServerInstance->AddCommand(command_rconnect); + command_rsquit = new cmd_rsquit(ServerInstance, this, Utils); + ServerInstance->AddCommand(command_rsquit); + if (Utils->EnableTimeSync) + { + SyncTimer = new TimeSyncTimer(ServerInstance, this); + ServerInstance->Timers->AddTimer(SyncTimer); + } + else + SyncTimer = NULL; + + RefreshTimer = new CacheRefreshTimer(ServerInstance, Utils); + ServerInstance->Timers->AddTimer(RefreshTimer); +} + +void ModuleSpanningTree::ShowLinks(TreeServer* Current, userrec* user, int hops) +{ + std::string Parent = Utils->TreeRoot->GetName(); + if (Current->GetParent()) + { + Parent = Current->GetParent()->GetName(); + } + for (unsigned int q = 0; q < Current->ChildCount(); q++) + { + if ((Current->GetChild(q)->Hidden) || ((Utils->HideULines) && (ServerInstance->ULine(Current->GetChild(q)->GetName().c_str())))) + { + if (*user->oper) + { + ShowLinks(Current->GetChild(q),user,hops+1); + } + } + else + { + ShowLinks(Current->GetChild(q),user,hops+1); + } + } + /* Don't display the line if its a uline, hide ulines is on, and the user isnt an oper */ + if ((Utils->HideULines) && (ServerInstance->ULine(Current->GetName().c_str())) && (!IS_OPER(user))) + return; + /* Or if the server is hidden and they're not an oper */ + else if ((Current->Hidden) && (!IS_OPER(user))) + return; + + user->WriteServ("364 %s %s %s :%d %s", user->nick,Current->GetName().c_str(), + (Utils->FlatLinks && (!IS_OPER(user))) ? ServerInstance->Config->ServerName : Parent.c_str(), + (Utils->FlatLinks && (!IS_OPER(user))) ? 0 : hops, + Current->GetDesc().c_str()); +} + +int ModuleSpanningTree::CountLocalServs() +{ + return Utils->TreeRoot->ChildCount(); +} + +int ModuleSpanningTree::CountServs() +{ + return Utils->serverlist.size(); +} + +void ModuleSpanningTree::HandleLinks(const char** parameters, int pcnt, userrec* user) +{ + ShowLinks(Utils->TreeRoot,user,0); + user->WriteServ("365 %s * :End of /LINKS list.",user->nick); + return; +} + +void ModuleSpanningTree::HandleLusers(const char** parameters, int pcnt, userrec* user) +{ + unsigned int n_users = ServerInstance->UserCount(); + + /* Only update these when someone wants to see them, more efficient */ + if ((unsigned int)ServerInstance->LocalUserCount() > max_local) + max_local = ServerInstance->LocalUserCount(); + if (n_users > max_global) + max_global = n_users; + + unsigned int ulined_count = 0; + unsigned int ulined_local_count = 0; + + /* If ulined are hidden and we're not an oper, count the number of ulined servers hidden, + * locally and globally (locally means directly connected to us) + */ + if ((Utils->HideULines) && (!*user->oper)) + { + for (server_hash::iterator q = Utils->serverlist.begin(); q != Utils->serverlist.end(); q++) + { + if (ServerInstance->ULine(q->second->GetName().c_str())) + { + ulined_count++; + if (q->second->GetParent() == Utils->TreeRoot) + ulined_local_count++; + } + } + } + user->WriteServ("251 %s :There are %d users and %d invisible on %d servers",user->nick,n_users-ServerInstance->InvisibleUserCount(),ServerInstance->InvisibleUserCount(),ulined_count ? this->CountServs() - ulined_count : this->CountServs()); + if (ServerInstance->OperCount()) + user->WriteServ("252 %s %d :operator(s) online",user->nick,ServerInstance->OperCount()); + if (ServerInstance->UnregisteredUserCount()) + user->WriteServ("253 %s %d :unknown connections",user->nick,ServerInstance->UnregisteredUserCount()); + if (ServerInstance->ChannelCount()) + user->WriteServ("254 %s %d :channels formed",user->nick,ServerInstance->ChannelCount()); + user->WriteServ("255 %s :I have %d clients and %d servers",user->nick,ServerInstance->LocalUserCount(),ulined_local_count ? this->CountLocalServs() - ulined_local_count : this->CountLocalServs()); + user->WriteServ("265 %s :Current Local Users: %d Max: %d",user->nick,ServerInstance->LocalUserCount(),max_local); + user->WriteServ("266 %s :Current Global Users: %d Max: %d",user->nick,n_users,max_global); + return; +} + +std::string ModuleSpanningTree::TimeToStr(time_t secs) +{ + time_t mins_up = secs / 60; + time_t hours_up = mins_up / 60; + time_t days_up = hours_up / 24; + secs = secs % 60; + mins_up = mins_up % 60; + hours_up = hours_up % 24; + return ((days_up ? (ConvToStr(days_up) + "d") : std::string("")) + + (hours_up ? (ConvToStr(hours_up) + "h") : std::string("")) + + (mins_up ? (ConvToStr(mins_up) + "m") : std::string("")) + + ConvToStr(secs) + "s"); +} + +const std::string ModuleSpanningTree::MapOperInfo(TreeServer* Current) +{ + time_t secs_up = ServerInstance->Time() - Current->age; + return (" [Up: " + TimeToStr(secs_up) + " Lag: "+ConvToStr(Current->rtt)+"s]"); +} + +// WARNING: NOT THREAD SAFE - DONT GET ANY SMART IDEAS. +void ModuleSpanningTree::ShowMap(TreeServer* Current, userrec* user, int depth, char matrix[128][128], float &totusers, float &totservers) +{ + if (line < 128) + { + for (int t = 0; t < depth; t++) + { + matrix[line][t] = ' '; + } + // 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[128]; + /* Neat and tidy default values, as we're dealing with a matrix not a simple string */ + memset(text, 0, 128); + + if (ServerInstance->clientlist->size() == 0) { + // If there are no users, WHO THE HELL DID THE /MAP?!?!?! + percent = 0; + } + else + { + percent = ((float)Current->GetUserCount() / (float)ServerInstance->clientlist->size()) * 100; + } + const std::string operdata = IS_OPER(user) ? MapOperInfo(Current) : ""; + snprintf(text, 126, "%s %s%5d [%5.2f%%]%s", Current->GetName().c_str(), spacer, Current->GetUserCount(), percent, operdata.c_str()); + totusers += Current->GetUserCount(); + totservers++; + strlcpy(&matrix[line][depth],text,126); + line++; + for (unsigned int q = 0; q < Current->ChildCount(); q++) + { + if ((Current->GetChild(q)->Hidden) || ((Utils->HideULines) && (ServerInstance->ULine(Current->GetChild(q)->GetName().c_str())))) + { + if (*user->oper) + { + ShowMap(Current->GetChild(q),user,(Utils->FlatLinks && (!*user->oper)) ? depth : depth+2,matrix,totusers,totservers); + } + } + else + { + ShowMap(Current->GetChild(q),user,(Utils->FlatLinks && (!*user->oper)) ? depth : depth+2,matrix,totusers,totservers); + } + } + } +} + +int ModuleSpanningTree::HandleMotd(const char** parameters, int pcnt, userrec* user) +{ + if (pcnt > 0) + { + if (match(ServerInstance->Config->ServerName, parameters[0])) + return 0; + + /* Remote MOTD, the server is within the 1st parameter */ + std::deque<std::string> params; + params.push_back(parameters[0]); + /* Send it out remotely, generate no reply yet */ + TreeServer* s = Utils->FindServerMask(parameters[0]); + if (s) + { + params[0] = s->GetName(); + Utils->DoOneToOne(user->nick, "MOTD", params, s->GetName()); + } + else + user->WriteServ( "402 %s %s :No such server", user->nick, parameters[0]); + return 1; + } + return 0; +} + +int ModuleSpanningTree::HandleAdmin(const char** parameters, int pcnt, userrec* user) +{ + if (pcnt > 0) + { + if (match(ServerInstance->Config->ServerName, parameters[0])) + return 0; + + /* Remote ADMIN, the server is within the 1st parameter */ + std::deque<std::string> params; + params.push_back(parameters[0]); + /* Send it out remotely, generate no reply yet */ + TreeServer* s = Utils->FindServerMask(parameters[0]); + if (s) + { + params[0] = s->GetName(); + Utils->DoOneToOne(user->nick, "ADMIN", params, s->GetName()); + } + else + user->WriteServ( "402 %s %s :No such server", user->nick, parameters[0]); + return 1; + } + return 0; +} + +int ModuleSpanningTree::HandleModules(const char** parameters, int pcnt, userrec* user) +{ + if (pcnt > 0) + { + if (match(ServerInstance->Config->ServerName, parameters[0])) + return 0; + + std::deque<std::string> params; + params.push_back(parameters[0]); + TreeServer* s = Utils->FindServerMask(parameters[0]); + if (s) + { + params[0] = s->GetName(); + Utils->DoOneToOne(user->nick, "MODULES", params, s->GetName()); + } + else + user->WriteServ( "402 %s %s :No such server", user->nick, parameters[0]); + return 1; + } + return 0; +} + +int ModuleSpanningTree::HandleStats(const char** parameters, int pcnt, userrec* user) +{ + if (pcnt > 1) + { + if (match(ServerInstance->Config->ServerName, parameters[1])) + return 0; + + /* Remote STATS, the server is within the 2nd parameter */ + std::deque<std::string> params; + params.push_back(parameters[0]); + params.push_back(parameters[1]); + /* Send it out remotely, generate no reply yet */ + + TreeServer* s = Utils->FindServerMask(parameters[1]); + if (s) + { + params[1] = s->GetName(); + Utils->DoOneToOne(user->nick, "STATS", params, s->GetName()); + } + else + { + user->WriteServ( "402 %s %s :No such server", user->nick, parameters[1]); + } + return 1; + } + return 0; +} + +// Ok, prepare to be confused. +// After much mulling over how to approach this, it struck me that +// the 'usual' way of doing a /MAP isnt the best way. Instead of +// keeping track of a ton of ascii characters, and line by line +// under recursion working out where to place them using multiplications +// and divisons, we instead render the map onto a backplane of characters +// (a character matrix), then draw the branches as a series of "L" shapes +// from the nodes. This is not only friendlier on CPU it uses less stack. +void ModuleSpanningTree::HandleMap(const char** parameters, int pcnt, userrec* user) +{ + // This array represents a virtual screen which we will + // "scratch" draw to, as the console device of an irc + // client does not provide for a proper terminal. + float totusers = 0; + float totservers = 0; + char matrix[128][128]; + for (unsigned int t = 0; t < 128; t++) + { + matrix[t][0] = '\0'; + } + line = 0; + // The only recursive bit is called here. + ShowMap(Utils->TreeRoot,user,0,matrix,totusers,totservers); + // Process each line one by one. The algorithm has a limit of + // 128 servers (which is far more than a spanning tree should have + // anyway, so we're ok). This limit can be raised simply by making + // the character matrix deeper, 128 rows taking 10k of memory. + for (int l = 1; l < line; l++) + { + // scan across the line looking for the start of the + // servername (the recursive part of the algorithm has placed + // the servers at indented positions depending on what they + // are related to) + int first_nonspace = 0; + while (matrix[l][first_nonspace] == ' ') + { + first_nonspace++; + } + first_nonspace--; + // Draw the `- (corner) section: this may be overwritten by + // another L shape passing along the same vertical pane, becoming + // a |- (branch) section instead. + matrix[l][first_nonspace] = '-'; + matrix[l][first_nonspace-1] = '`'; + int l2 = l - 1; + // Draw upwards until we hit the parent server, causing possibly + // other corners (`-) to become branches (|-) + while ((matrix[l2][first_nonspace-1] == ' ') || (matrix[l2][first_nonspace-1] == '`')) + { + matrix[l2][first_nonspace-1] = '|'; + l2--; + } + } + // dump the whole lot to the user. This is the easy bit, honest. + for (int t = 0; t < line; t++) + { + user->WriteServ("006 %s :%s",user->nick,&matrix[t][0]); + } + float avg_users = totusers / totservers; + user->WriteServ("270 %s :%.0f server%s and %.0f user%s, average %.2f users per server",user->nick,totservers,(totservers > 1 ? "s" : ""),totusers,(totusers > 1 ? "s" : ""),avg_users); + user->WriteServ("007 %s :End of /MAP",user->nick); + return; +} + +int ModuleSpanningTree::HandleSquit(const char** parameters, int pcnt, userrec* user) +{ + TreeServer* s = Utils->FindServerMask(parameters[0]); + if (s) + { + if (s == Utils->TreeRoot) + { + user->WriteServ("NOTICE %s :*** SQUIT: Foolish mortal, you cannot make a server SQUIT itself! (%s matches local server name)",user->nick,parameters[0]); + return 1; + } + TreeSocket* sock = s->GetSocket(); + if (sock) + { + ServerInstance->SNO->WriteToSnoMask('l',"SQUIT: Server \002%s\002 removed from network by %s",parameters[0],user->nick); + sock->Squit(s,std::string("Server quit by ") + user->GetFullRealHost()); + ServerInstance->SE->DelFd(sock); + sock->Close(); + } + else + { + if (IS_LOCAL(user)) + user->WriteServ("NOTICE %s :*** WARNING: Using SQUIT to split remote servers is deprecated. Please use RSQUIT instead.",user->nick); + } + } + else + { + user->WriteServ("NOTICE %s :*** SQUIT: The server \002%s\002 does not exist on the network.",user->nick,parameters[0]); + } + return 1; +} + +int ModuleSpanningTree::HandleTime(const char** parameters, int pcnt, userrec* user) +{ + if ((IS_LOCAL(user)) && (pcnt)) + { + TreeServer* found = Utils->FindServerMask(parameters[0]); + if (found) + { + // we dont' override for local server + if (found == Utils->TreeRoot) + return 0; + + std::deque<std::string> params; + params.push_back(found->GetName()); + params.push_back(user->nick); + Utils->DoOneToOne(ServerInstance->Config->ServerName,"TIME",params,found->GetName()); + } + else + { + user->WriteServ("402 %s %s :No such server",user->nick,parameters[0]); + } + } + return 1; +} + +int ModuleSpanningTree::HandleRemoteWhois(const char** parameters, int pcnt, userrec* user) +{ + if ((IS_LOCAL(user)) && (pcnt > 1)) + { + userrec* remote = ServerInstance->FindNick(parameters[1]); + if ((remote) && (remote->GetFd() < 0)) + { + std::deque<std::string> params; + params.push_back(parameters[1]); + Utils->DoOneToOne(user->nick,"IDLE",params,remote->server); + return 1; + } + else if (!remote) + { + user->WriteServ("401 %s %s :No such nick/channel",user->nick, parameters[1]); + user->WriteServ("318 %s %s :End of /WHOIS list.",user->nick, parameters[1]); + return 1; + } + } + return 0; +} + +void ModuleSpanningTree::DoPingChecks(time_t curtime) +{ + for (unsigned int j = 0; j < Utils->TreeRoot->ChildCount(); j++) + { + TreeServer* serv = Utils->TreeRoot->GetChild(j); + TreeSocket* sock = serv->GetSocket(); + if (sock) + { + if (curtime >= serv->NextPingTime()) + { + if (serv->AnsweredLastPing()) + { + sock->WriteLine(std::string(":")+ServerInstance->Config->ServerName+" PING "+serv->GetName()); + serv->SetNextPingTime(curtime + 60); + serv->LastPing = curtime; + serv->Warned = false; + } + else + { + /* they didnt answer, boot them */ + sock->SendError("Ping timeout"); + sock->Squit(serv,"Ping timeout"); + /*** XXX SOCKET CULL ***/ + return; + } + } + else if ((Utils->PingWarnTime) && (!serv->Warned) && (curtime >= serv->NextPingTime() - (60 - Utils->PingWarnTime)) && (!serv->AnsweredLastPing())) + { + /* The server hasnt responded, send a warning to opers */ + ServerInstance->SNO->WriteToSnoMask('l',"Server \002%s\002 has not responded to PING for %d seconds, high latency.", serv->GetName().c_str(), Utils->PingWarnTime); + serv->Warned = true; + } + } + } + + /* Cancel remote burst mode on any servers which still have it enabled due to latency/lack of data. + * This prevents lost REMOTECONNECT notices + */ + for (server_hash::iterator i = Utils->serverlist.begin(); i != Utils->serverlist.end(); i++) + Utils->SetRemoteBursting(i->second, false); +} + +void ModuleSpanningTree::ConnectServer(Link* x) +{ + bool ipvalid = true; + QueryType start_type = DNS_QUERY_A; +#ifdef IPV6 + start_type = DNS_QUERY_AAAA; + if (strchr(x->IPAddr.c_str(),':')) + { + in6_addr n; + if (inet_pton(AF_INET6, x->IPAddr.c_str(), &n) < 1) + ipvalid = false; + } + else +#endif + { + in_addr n; + if (inet_aton(x->IPAddr.c_str(),&n) < 1) + ipvalid = false; + } + + /* Do we already have an IP? If so, no need to resolve it. */ + if (ipvalid) + { + /* Gave a hook, but it wasnt one we know */ + if ((!x->Hook.empty()) && (Utils->hooks.find(x->Hook.c_str()) == Utils->hooks.end())) + return; + TreeSocket* newsocket = new TreeSocket(Utils, ServerInstance, x->IPAddr,x->Port,false,x->Timeout ? x->Timeout : 10,x->Name.c_str(), x->Bind, x->Hook.empty() ? NULL : Utils->hooks[x->Hook.c_str()]); + if (newsocket->GetFd() > -1) + { + /* Handled automatically on success */ + } + else + { + ServerInstance->SNO->WriteToSnoMask('l',"CONNECT: Error connecting \002%s\002: %s.",x->Name.c_str(),strerror(errno)); + delete newsocket; + Utils->DoFailOver(x); + } + } + else + { + try + { + bool cached; + ServernameResolver* snr = new ServernameResolver((Module*)this, Utils, ServerInstance,x->IPAddr, *x, cached, start_type); + ServerInstance->AddResolver(snr, cached); + } + catch (ModuleException& e) + { + ServerInstance->SNO->WriteToSnoMask('l',"CONNECT: Error connecting \002%s\002: %s.",x->Name.c_str(), e.GetReason()); + Utils->DoFailOver(x); + } + } +} + +void ModuleSpanningTree::AutoConnectServers(time_t curtime) +{ + for (std::vector<Link>::iterator x = Utils->LinkBlocks.begin(); x < Utils->LinkBlocks.end(); x++) + { + if ((x->AutoConnect) && (curtime >= x->NextConnectTime)) + { + x->NextConnectTime = curtime + x->AutoConnect; + TreeServer* CheckDupe = Utils->FindServer(x->Name.c_str()); + if (x->FailOver.length()) + { + TreeServer* CheckFailOver = Utils->FindServer(x->FailOver.c_str()); + if (CheckFailOver) + { + /* The failover for this server is currently a member of the network. + * The failover probably succeeded, where the main link did not. + * Don't try the main link until the failover is gone again. + */ + continue; + } + } + if (!CheckDupe) + { + // an autoconnected server is not connected. Check if its time to connect it + ServerInstance->SNO->WriteToSnoMask('l',"AUTOCONNECT: Auto-connecting server \002%s\002 (%lu seconds until next attempt)",x->Name.c_str(),x->AutoConnect); + this->ConnectServer(&(*x)); + } + } + } +} + +int ModuleSpanningTree::HandleVersion(const char** parameters, int pcnt, userrec* user) +{ + // we've already checked if pcnt > 0, so this is safe + TreeServer* found = Utils->FindServerMask(parameters[0]); + if (found) + { + std::string Version = found->GetVersion(); + user->WriteServ("351 %s :%s",user->nick,Version.c_str()); + if (found == Utils->TreeRoot) + { + ServerInstance->Config->Send005(user); + } + } + else + { + user->WriteServ("402 %s %s :No such server",user->nick,parameters[0]); + } + return 1; +} + +int ModuleSpanningTree::HandleConnect(const char** parameters, int pcnt, userrec* user) +{ + for (std::vector<Link>::iterator x = Utils->LinkBlocks.begin(); x < Utils->LinkBlocks.end(); x++) + { + if (ServerInstance->MatchText(x->Name.c_str(),parameters[0])) + { + TreeServer* CheckDupe = Utils->FindServer(x->Name.c_str()); + if (!CheckDupe) + { + user->WriteServ("NOTICE %s :*** CONNECT: Connecting to server: \002%s\002 (%s:%d)",user->nick,x->Name.c_str(),(x->HiddenFromStats ? "<hidden>" : x->IPAddr.c_str()),x->Port); + ConnectServer(&(*x)); + return 1; + } + else + { + user->WriteServ("NOTICE %s :*** CONNECT: Server \002%s\002 already exists on the network and is connected via \002%s\002",user->nick,x->Name.c_str(),CheckDupe->GetParent()->GetName().c_str()); + return 1; + } + } + } + user->WriteServ("NOTICE %s :*** CONNECT: No server matching \002%s\002 could be found in the config file.",user->nick,parameters[0]); + return 1; +} + +void ModuleSpanningTree::BroadcastTimeSync() +{ + if (Utils->MasterTime) + { + std::deque<std::string> params; + params.push_back(ConvToStr(ServerInstance->Time(false))); + params.push_back("FORCE"); + Utils->DoOneToMany(Utils->TreeRoot->GetName(), "TIMESET", params); + } +} + +int ModuleSpanningTree::OnStats(char statschar, userrec* user, string_list &results) +{ + if ((statschar == 'c') || (statschar == 'n')) + { + for (unsigned int i = 0; i < Utils->LinkBlocks.size(); i++) + { + results.push_back(std::string(ServerInstance->Config->ServerName)+" 213 "+user->nick+" "+statschar+" *@"+(Utils->LinkBlocks[i].HiddenFromStats ? "<hidden>" : Utils->LinkBlocks[i].IPAddr)+" * "+Utils->LinkBlocks[i].Name.c_str()+" "+ConvToStr(Utils->LinkBlocks[i].Port)+" "+(Utils->LinkBlocks[i].Hook.empty() ? "plaintext" : Utils->LinkBlocks[i].Hook)+" "+(Utils->LinkBlocks[i].AutoConnect ? 'a' : '-')+'s'); + if (statschar == 'c') + results.push_back(std::string(ServerInstance->Config->ServerName)+" 244 "+user->nick+" H * * "+Utils->LinkBlocks[i].Name.c_str()); + } + results.push_back(std::string(ServerInstance->Config->ServerName)+" 219 "+user->nick+" "+statschar+" :End of /STATS report"); + ServerInstance->SNO->WriteToSnoMask('t',"%s '%c' requested by %s (%s@%s)",(!strcmp(user->server,ServerInstance->Config->ServerName) ? "Stats" : "Remote stats"),statschar,user->nick,user->ident,user->host); + return 1; + } + + if (statschar == 'p') + { + /* show all server ports, after showing client ports. -- w00t */ + + for (unsigned int i = 0; i < Utils->Bindings.size(); i++) + { + std::string ip = Utils->Bindings[i]->IP; + if (ip.empty()) + ip = "*"; + + std::string transport("plaintext"); + if (Utils->Bindings[i]->GetHook()) + transport = InspSocketNameRequest(this, Utils->Bindings[i]->GetHook()).Send(); + + results.push_back(ConvToStr(ServerInstance->Config->ServerName) + " 249 "+user->nick+" :" + ip + ":" + ConvToStr(Utils->Bindings[i]->port)+ + " (server, " + transport + ")"); + } + } + return 0; +} + +int ModuleSpanningTree::OnPreCommand(const std::string &command, const char** parameters, int pcnt, userrec *user, bool validated, const std::string &original_line) +{ + /* If the command doesnt appear to be valid, we dont want to mess with it. */ + if (!validated) + return 0; + + if (command == "CONNECT") + { + return this->HandleConnect(parameters,pcnt,user); + } + else if (command == "STATS") + { + return this->HandleStats(parameters,pcnt,user); + } + else if (command == "MOTD") + { + return this->HandleMotd(parameters,pcnt,user); + } + else if (command == "ADMIN") + { + return this->HandleAdmin(parameters,pcnt,user); + } + else if (command == "SQUIT") + { + return this->HandleSquit(parameters,pcnt,user); + } + else if (command == "MAP") + { + this->HandleMap(parameters,pcnt,user); + return 1; + } + else if ((command == "TIME") && (pcnt > 0)) + { + return this->HandleTime(parameters,pcnt,user); + } + else if (command == "LUSERS") + { + this->HandleLusers(parameters,pcnt,user); + return 1; + } + else if (command == "LINKS") + { + this->HandleLinks(parameters,pcnt,user); + return 1; + } + else if (command == "WHOIS") + { + if (pcnt > 1) + { + // remote whois + return this->HandleRemoteWhois(parameters,pcnt,user); + } + } + else if ((command == "VERSION") && (pcnt > 0)) + { + this->HandleVersion(parameters,pcnt,user); + return 1; + } + else if ((command == "MODULES") && (pcnt > 0)) + { + return this->HandleModules(parameters,pcnt,user); + } + return 0; +} + +void ModuleSpanningTree::OnPostCommand(const std::string &command, const char** parameters, int pcnt, userrec *user, CmdResult result, const std::string &original_line) +{ + if ((result == CMD_SUCCESS) && (ServerInstance->IsValidModuleCommand(command, pcnt, user))) + { + // this bit of code cleverly routes all module commands + // to all remote severs *automatically* so that modules + // can just handle commands locally, without having + // to have any special provision in place for remote + // commands and linking protocols. + std::deque<std::string> params; + params.clear(); + for (int j = 0; j < pcnt; j++) + { + if (strchr(parameters[j],' ')) + { + params.push_back(":" + std::string(parameters[j])); + } + else + { + params.push_back(std::string(parameters[j])); + } + } + Utils->DoOneToMany(user->nick,command,params); + } +} + +void ModuleSpanningTree::OnGetServerDescription(const std::string &servername,std::string &description) +{ + TreeServer* s = Utils->FindServer(servername); + if (s) + { + description = s->GetDesc(); + } +} + +void ModuleSpanningTree::OnUserInvite(userrec* source,userrec* dest,chanrec* channel) +{ + if (IS_LOCAL(source)) + { + std::deque<std::string> params; + params.push_back(dest->nick); + params.push_back(channel->name); + Utils->DoOneToMany(source->nick,"INVITE",params); + } +} + +void ModuleSpanningTree::OnPostLocalTopicChange(userrec* user, chanrec* chan, const std::string &topic) +{ + std::deque<std::string> params; + params.push_back(chan->name); + params.push_back(":"+topic); + Utils->DoOneToMany(user->nick,"TOPIC",params); +} + +void ModuleSpanningTree::OnWallops(userrec* user, const std::string &text) +{ + if (IS_LOCAL(user)) + { + std::deque<std::string> params; + params.push_back(":"+text); + Utils->DoOneToMany(user->nick,"WALLOPS",params); + } +} + +void ModuleSpanningTree::OnUserNotice(userrec* user, void* dest, int target_type, const std::string &text, char status, const CUList &exempt_list) +{ + if (target_type == TYPE_USER) + { + userrec* d = (userrec*)dest; + if ((d->GetFd() < 0) && (IS_LOCAL(user))) + { + std::deque<std::string> params; + params.clear(); + params.push_back(d->nick); + params.push_back(":"+text); + Utils->DoOneToOne(user->nick,"NOTICE",params,d->server); + } + } + else if (target_type == TYPE_CHANNEL) + { + if (IS_LOCAL(user)) + { + chanrec *c = (chanrec*)dest; + if (c) + { + std::string cname = c->name; + if (status) + cname = status + cname; + TreeServerList list; + Utils->GetListOfServersForChannel(c,list,status,exempt_list); + for (TreeServerList::iterator i = list.begin(); i != list.end(); i++) + { + TreeSocket* Sock = i->second->GetSocket(); + if (Sock) + Sock->WriteLine(":"+std::string(user->nick)+" NOTICE "+cname+" :"+text); + } + } + } + } + else if (target_type == TYPE_SERVER) + { + if (IS_LOCAL(user)) + { + char* target = (char*)dest; + std::deque<std::string> par; + par.push_back(target); + par.push_back(":"+text); + Utils->DoOneToMany(user->nick,"NOTICE",par); + } + } +} + +void ModuleSpanningTree::OnUserMessage(userrec* user, void* dest, int target_type, const std::string &text, char status, const CUList &exempt_list) +{ + if (target_type == TYPE_USER) + { + // route private messages which are targetted at clients only to the server + // which needs to receive them + userrec* d = (userrec*)dest; + if ((d->GetFd() < 0) && (IS_LOCAL(user))) + { + std::deque<std::string> params; + params.clear(); + params.push_back(d->nick); + params.push_back(":"+text); + Utils->DoOneToOne(user->nick,"PRIVMSG",params,d->server); + } + } + else if (target_type == TYPE_CHANNEL) + { + if (IS_LOCAL(user)) + { + chanrec *c = (chanrec*)dest; + if (c) + { + std::string cname = c->name; + if (status) + cname = status + cname; + TreeServerList list; + Utils->GetListOfServersForChannel(c,list,status,exempt_list); + for (TreeServerList::iterator i = list.begin(); i != list.end(); i++) + { + TreeSocket* Sock = i->second->GetSocket(); + if (Sock) + Sock->WriteLine(":"+std::string(user->nick)+" PRIVMSG "+cname+" :"+text); + } + } + } + } + else if (target_type == TYPE_SERVER) + { + if (IS_LOCAL(user)) + { + char* target = (char*)dest; + std::deque<std::string> par; + par.push_back(target); + par.push_back(":"+text); + Utils->DoOneToMany(user->nick,"PRIVMSG",par); + } + } +} + +void ModuleSpanningTree::OnBackgroundTimer(time_t curtime) +{ + AutoConnectServers(curtime); + DoPingChecks(curtime); +} + +void ModuleSpanningTree::OnUserJoin(userrec* user, chanrec* channel, bool &silent) +{ + // Only do this for local users + if (IS_LOCAL(user)) + { + if (channel->GetUserCounter() == 1) + { + std::deque<std::string> params; + // set up their permissions and the channel TS with FJOIN. + // All users are FJOINed now, because a module may specify + // new joining permissions for the user. + params.push_back(channel->name); + params.push_back(ConvToStr(channel->age)); + params.push_back(std::string(channel->GetAllPrefixChars(user))+","+std::string(user->nick)); + Utils->DoOneToMany(ServerInstance->Config->ServerName,"FJOIN",params); + /* First user in, sync the modes for the channel */ + params.pop_back(); + params.push_back(channel->ChanModes(true)); + Utils->DoOneToMany(ServerInstance->Config->ServerName,"FMODE",params); + } + else + { + std::deque<std::string> params; + params.push_back(channel->name); + params.push_back(ConvToStr(channel->age)); + Utils->DoOneToMany(user->nick,"JOIN",params); + } + } +} + +void ModuleSpanningTree::OnChangeHost(userrec* user, const std::string &newhost) +{ + // only occurs for local clients + if (user->registered != REG_ALL) + return; + std::deque<std::string> params; + params.push_back(newhost); + Utils->DoOneToMany(user->nick,"FHOST",params); +} + +void ModuleSpanningTree::OnChangeName(userrec* user, const std::string &gecos) +{ + // only occurs for local clients + if (user->registered != REG_ALL) + return; + std::deque<std::string> params; + params.push_back(gecos); + Utils->DoOneToMany(user->nick,"FNAME",params); +} + +void ModuleSpanningTree::OnUserPart(userrec* user, chanrec* channel, const std::string &partmessage, bool &silent) +{ + if (IS_LOCAL(user)) + { + std::deque<std::string> params; + params.push_back(channel->name); + if (!partmessage.empty()) + params.push_back(":"+partmessage); + Utils->DoOneToMany(user->nick,"PART",params); + } +} + +void ModuleSpanningTree::OnUserConnect(userrec* user) +{ + char agestr[MAXBUF]; + if (IS_LOCAL(user)) + { + std::deque<std::string> params; + snprintf(agestr,MAXBUF,"%lu",(unsigned long)user->age); + params.push_back(agestr); + params.push_back(user->nick); + params.push_back(user->host); + params.push_back(user->dhost); + params.push_back(user->ident); + params.push_back("+"+std::string(user->FormatModes())); + params.push_back(user->GetIPString()); + params.push_back(":"+std::string(user->fullname)); + Utils->DoOneToMany(ServerInstance->Config->ServerName,"NICK",params); + // User is Local, change needs to be reflected! + TreeServer* SourceServer = Utils->FindServer(user->server); + if (SourceServer) + { + SourceServer->AddUserCount(); + } + } +} + +void ModuleSpanningTree::OnUserQuit(userrec* user, const std::string &reason, const std::string &oper_message) +{ + if ((IS_LOCAL(user)) && (user->registered == REG_ALL)) + { + std::deque<std::string> params; + + if (oper_message != reason) + { + params.push_back(":"+oper_message); + Utils->DoOneToMany(user->nick,"OPERQUIT",params); + } + params.clear(); + params.push_back(":"+reason); + Utils->DoOneToMany(user->nick,"QUIT",params); + } + // Regardless, We need to modify the user Counts.. + TreeServer* SourceServer = Utils->FindServer(user->server); + if (SourceServer) + { + SourceServer->DelUserCount(); + } +} + +void ModuleSpanningTree::OnUserPostNick(userrec* user, const std::string &oldnick) +{ + if (IS_LOCAL(user)) + { + std::deque<std::string> params; + params.push_back(user->nick); + Utils->DoOneToMany(oldnick,"NICK",params); + } +} + +void ModuleSpanningTree::OnUserKick(userrec* source, userrec* user, chanrec* chan, const std::string &reason, bool &silent) +{ + if ((source) && (IS_LOCAL(source))) + { + std::deque<std::string> params; + params.push_back(chan->name); + params.push_back(user->nick); + params.push_back(":"+reason); + Utils->DoOneToMany(source->nick,"KICK",params); + } + else if (!source) + { + std::deque<std::string> params; + params.push_back(chan->name); + params.push_back(user->nick); + params.push_back(":"+reason); + Utils->DoOneToMany(ServerInstance->Config->ServerName,"KICK",params); + } +} + +void ModuleSpanningTree::OnRemoteKill(userrec* source, userrec* dest, const std::string &reason, const std::string &operreason) +{ + std::deque<std::string> params; + params.push_back(":"+reason); + Utils->DoOneToMany(dest->nick,"OPERQUIT",params); + params.clear(); + params.push_back(dest->nick); + params.push_back(":"+reason); + dest->SetOperQuit(operreason); + Utils->DoOneToMany(source->nick,"KILL",params); +} + +void ModuleSpanningTree::OnRehash(userrec* user, const std::string ¶meter) +{ + if (!parameter.empty()) + { + std::deque<std::string> params; + params.push_back(parameter); + Utils->DoOneToMany(user ? user->nick : ServerInstance->Config->ServerName, "REHASH", params); + // check for self + if (ServerInstance->MatchText(ServerInstance->Config->ServerName,parameter)) + { + ServerInstance->WriteOpers("*** Remote rehash initiated locally by \002%s\002", user ? user->nick : ServerInstance->Config->ServerName); + ServerInstance->RehashServer(); + } + } + Utils->ReadConfiguration(false); + InitializeDisabledCommands(ServerInstance->Config->DisabledCommands, ServerInstance); +} + +// note: the protocol does not allow direct umode +o except +// via NICK with 8 params. sending OPERTYPE infers +o modechange +// locally. +void ModuleSpanningTree::OnOper(userrec* user, const std::string &opertype) +{ + if (IS_LOCAL(user)) + { + std::deque<std::string> params; + params.push_back(opertype); + Utils->DoOneToMany(user->nick,"OPERTYPE",params); + } +} + +void ModuleSpanningTree::OnLine(userrec* source, const std::string &host, bool adding, char linetype, long duration, const std::string &reason) +{ + if (!source) + { + /* Server-set lines */ + char data[MAXBUF]; + snprintf(data,MAXBUF,"%c %s %s %lu %lu :%s", linetype, host.c_str(), ServerInstance->Config->ServerName, (unsigned long)ServerInstance->Time(false), + (unsigned long)duration, reason.c_str()); + std::deque<std::string> params; + params.push_back(data); + Utils->DoOneToMany(ServerInstance->Config->ServerName, "ADDLINE", params); + } + else + { + if (IS_LOCAL(source)) + { + char type[8]; + snprintf(type,8,"%cLINE",linetype); + std::string stype = type; + if (adding) + { + char sduration[MAXBUF]; + snprintf(sduration,MAXBUF,"%ld",duration); + std::deque<std::string> params; + params.push_back(host); + params.push_back(sduration); + params.push_back(":"+reason); + Utils->DoOneToMany(source->nick,stype,params); + } + else + { + std::deque<std::string> params; + params.push_back(host); + Utils->DoOneToMany(source->nick,stype,params); + } + } + } +} + +void ModuleSpanningTree::OnAddGLine(long duration, userrec* source, const std::string &reason, const std::string &hostmask) +{ + OnLine(source,hostmask,true,'G',duration,reason); +} + +void ModuleSpanningTree::OnAddZLine(long duration, userrec* source, const std::string &reason, const std::string &ipmask) +{ + OnLine(source,ipmask,true,'Z',duration,reason); +} + +void ModuleSpanningTree::OnAddQLine(long duration, userrec* source, const std::string &reason, const std::string &nickmask) +{ + OnLine(source,nickmask,true,'Q',duration,reason); +} + +void ModuleSpanningTree::OnAddELine(long duration, userrec* source, const std::string &reason, const std::string &hostmask) +{ + OnLine(source,hostmask,true,'E',duration,reason); +} + +void ModuleSpanningTree::OnDelGLine(userrec* source, const std::string &hostmask) +{ + OnLine(source,hostmask,false,'G',0,""); +} + +void ModuleSpanningTree::OnDelZLine(userrec* source, const std::string &ipmask) +{ + OnLine(source,ipmask,false,'Z',0,""); +} + +void ModuleSpanningTree::OnDelQLine(userrec* source, const std::string &nickmask) +{ + OnLine(source,nickmask,false,'Q',0,""); +} + +void ModuleSpanningTree::OnDelELine(userrec* source, const std::string &hostmask) +{ + OnLine(source,hostmask,false,'E',0,""); +} + +void ModuleSpanningTree::OnMode(userrec* user, void* dest, int target_type, const std::string &text) +{ + if ((IS_LOCAL(user)) && (user->registered == REG_ALL)) + { + std::deque<std::string> params; + std::string command; + + if (target_type == TYPE_USER) + { + userrec* u = (userrec*)dest; + params.push_back(u->nick); + params.push_back(text); + command = "MODE"; + } + else + { + chanrec* c = (chanrec*)dest; + params.push_back(c->name); + params.push_back(ConvToStr(c->age)); + params.push_back(text); + command = "FMODE"; + } + Utils->DoOneToMany(user->nick, command, params); + } +} + +void ModuleSpanningTree::OnSetAway(userrec* user) +{ + if (IS_LOCAL(user)) + { + std::deque<std::string> params; + params.push_back(":"+std::string(user->awaymsg)); + Utils->DoOneToMany(user->nick,"AWAY",params); + } +} + +void ModuleSpanningTree::OnCancelAway(userrec* user) +{ + if (IS_LOCAL(user)) + { + std::deque<std::string> params; + params.clear(); + Utils->DoOneToMany(user->nick,"AWAY",params); + } +} + +void ModuleSpanningTree::ProtoSendMode(void* opaque, int target_type, void* target, const std::string &modeline) +{ + TreeSocket* s = (TreeSocket*)opaque; + if (target) + { + if (target_type == TYPE_USER) + { + userrec* u = (userrec*)target; + s->WriteLine(std::string(":")+ServerInstance->Config->ServerName+" FMODE "+u->nick+" "+ConvToStr(u->age)+" "+modeline); + } + else + { + chanrec* c = (chanrec*)target; + s->WriteLine(std::string(":")+ServerInstance->Config->ServerName+" FMODE "+c->name+" "+ConvToStr(c->age)+" "+modeline); + } + } +} + +void ModuleSpanningTree::ProtoSendMetaData(void* opaque, int target_type, void* target, const std::string &extname, const std::string &extdata) +{ + TreeSocket* s = (TreeSocket*)opaque; + if (target) + { + if (target_type == TYPE_USER) + { + userrec* u = (userrec*)target; + s->WriteLine(std::string(":")+ServerInstance->Config->ServerName+" METADATA "+u->nick+" "+extname+" :"+extdata); + } + else if (target_type == TYPE_CHANNEL) + { + chanrec* c = (chanrec*)target; + s->WriteLine(std::string(":")+ServerInstance->Config->ServerName+" METADATA "+c->name+" "+extname+" :"+extdata); + } + } + if (target_type == TYPE_OTHER) + { + s->WriteLine(std::string(":")+ServerInstance->Config->ServerName+" METADATA * "+extname+" :"+extdata); + } +} + +void ModuleSpanningTree::OnEvent(Event* event) +{ + std::deque<std::string>* params = (std::deque<std::string>*)event->GetData(); + if (event->GetEventID() == "send_metadata") + { + if (params->size() < 3) + return; + (*params)[2] = ":" + (*params)[2]; + Utils->DoOneToMany(ServerInstance->Config->ServerName,"METADATA",*params); + } + else if (event->GetEventID() == "send_topic") + { + if (params->size() < 2) + return; + (*params)[1] = ":" + (*params)[1]; + params->insert(params->begin() + 1,ServerInstance->Config->ServerName); + params->insert(params->begin() + 1,ConvToStr(ServerInstance->Time(true))); + Utils->DoOneToMany(ServerInstance->Config->ServerName,"FTOPIC",*params); + } + else if (event->GetEventID() == "send_mode") + { + if (params->size() < 2) + return; + // Insert the TS value of the object, either userrec or chanrec + time_t ourTS = 0; + userrec* a = ServerInstance->FindNick((*params)[0]); + if (a) + { + ourTS = a->age; + Utils->DoOneToMany(ServerInstance->Config->ServerName,"MODE",*params); + return; + } + else + { + chanrec* a = ServerInstance->FindChan((*params)[0]); + if (a) + { + ourTS = a->age; + params->insert(params->begin() + 1,ConvToStr(ourTS)); + Utils->DoOneToMany(ServerInstance->Config->ServerName,"FMODE",*params); + } + } + } + else if (event->GetEventID() == "send_mode_explicit") + { + if (params->size() < 2) + return; + Utils->DoOneToMany(ServerInstance->Config->ServerName,"MODE",*params); + } + else if (event->GetEventID() == "send_opers") + { + if (params->size() < 1) + return; + (*params)[0] = ":" + (*params)[0]; + Utils->DoOneToMany(ServerInstance->Config->ServerName,"OPERNOTICE",*params); + } + else if (event->GetEventID() == "send_modeset") + { + if (params->size() < 2) + return; + (*params)[1] = ":" + (*params)[1]; + Utils->DoOneToMany(ServerInstance->Config->ServerName,"MODENOTICE",*params); + } + else if (event->GetEventID() == "send_snoset") + { + if (params->size() < 2) + return; + (*params)[1] = ":" + (*params)[1]; + Utils->DoOneToMany(ServerInstance->Config->ServerName,"SNONOTICE",*params); + } + else if (event->GetEventID() == "send_push") + { + if (params->size() < 2) + return; + + userrec *a = ServerInstance->FindNick((*params)[0]); + + if (!a) + return; + + (*params)[1] = ":" + (*params)[1]; + Utils->DoOneToOne(ServerInstance->Config->ServerName, "PUSH", *params, a->server); + } +} + +ModuleSpanningTree::~ModuleSpanningTree() +{ + /* This will also free the listeners */ + delete Utils; + if (SyncTimer) + ServerInstance->Timers->DelTimer(SyncTimer); + + ServerInstance->Timers->DelTimer(RefreshTimer); + + ServerInstance->DoneWithInterface("InspSocketHook"); +} + +Version ModuleSpanningTree::GetVersion() +{ + return Version(1,1,0,2,VF_VENDOR,API_VERSION); +} + +void ModuleSpanningTree::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] = List[I_OnEvent] = List[I_OnSetAway] = List[I_OnCancelAway] = List[I_OnPostCommand] = 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 ModuleSpanningTree::Prioritize() +{ + return PRIORITY_LAST; +} + +MODULE_INIT(ModuleSpanningTree) + diff --git a/src/modules/m_spanningtree/main.h b/src/modules/m_spanningtree/main.h index 5bfb73e6a..c184ef076 100644 --- a/src/modules/m_spanningtree/main.h +++ b/src/modules/m_spanningtree/main.h @@ -1 +1,198 @@ -/* +------------------------------------+
* | 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.
*
* ---------------------------------------------------
*/
#ifndef __ST_MAIN__
#define __ST_MAIN__
#include "inspircd.h"
#include "modules.h"
/** If you make a change which breaks the protocol, increment this.
* If you completely change the protocol, completely change the number.
*
* IMPORTANT: If you make changes, document your changes here, without fail:
* http://www.inspircd.org/wiki/List_of_protocol_changes_between_versions
*
* Failure to document your protocol changes will result in a painfully
* painful death by pain. You have been warned.
*/
const long ProtocolVersion = 1105;
/** Forward declarations
*/
class cmd_rconnect;
class cmd_rsquit;
class SpanningTreeUtilities;
class TimeSyncTimer;
class CacheRefreshTimer;
class TreeServer;
class Link;
/** This is the main class for the spanningtree module
*/
class ModuleSpanningTree : public Module
{
int line;
int NumServers;
unsigned int max_local;
unsigned int max_global;
cmd_rconnect* command_rconnect;
cmd_rsquit* command_rsquit;
SpanningTreeUtilities* Utils;
public:
/** Timer for clock syncs
*/
TimeSyncTimer *SyncTimer;
CacheRefreshTimer *RefreshTimer;
/** Constructor
*/
ModuleSpanningTree(InspIRCd* Me);
/** Shows /LINKS
*/
void ShowLinks(TreeServer* Current, userrec* user, int hops);
/** Counts local servers
*/
int CountLocalServs();
/** Counts local and remote servers
*/
int CountServs();
/** Handle LINKS command
*/
void HandleLinks(const char** parameters, int pcnt, userrec* user);
/** Handle LUSERS command
*/
void HandleLusers(const char** parameters, int pcnt, userrec* user);
/** Show MAP output to a user (recursive)
*/
void ShowMap(TreeServer* Current, userrec* user, int depth, char matrix[128][128], float &totusers, float &totservers);
/** Handle remote MOTD
*/
int HandleMotd(const char** parameters, int pcnt, userrec* user);
/** Handle remote ADMIN
*/
int HandleAdmin(const char** parameters, int pcnt, userrec* user);
/** Handle remote STATS
*/
int HandleStats(const char** parameters, int pcnt, userrec* user);
/** Handle MAP command
*/
void HandleMap(const char** parameters, int pcnt, userrec* user);
/** Handle SQUIT
*/
int HandleSquit(const char** parameters, int pcnt, userrec* user);
/** Handle TIME
*/
int HandleTime(const char** parameters, int pcnt, userrec* user);
/** Handle remote WHOIS
*/
int HandleRemoteWhois(const char** parameters, int pcnt, userrec* user);
/** Handle remote MODULES
*/
int HandleModules(const char** parameters, int pcnt, userrec* user);
/** Ping all local servers
*/
void DoPingChecks(time_t curtime);
/** Connect a server locally
*/
void ConnectServer(Link* x);
/** Check if any servers are due to be autoconnected
*/
void AutoConnectServers(time_t curtime);
/** Handle remote VERSON
*/
int HandleVersion(const char** parameters, int pcnt, userrec* user);
/** Handle CONNECT
*/
int HandleConnect(const char** parameters, int pcnt, userrec* user);
/** Send out time sync to all servers
*/
void BroadcastTimeSync();
/** Returns oper-specific MAP information
*/
const std::string MapOperInfo(TreeServer* Current);
/** Display a time as a human readable string
*/
std::string TimeToStr(time_t secs);
/**
** *** MODULE EVENTS ***
**/
virtual int OnPreCommand(const std::string &command, const char** parameters, int pcnt, userrec *user, bool validated, const std::string &original_line);
virtual void OnPostCommand(const std::string &command, const char** parameters, int pcnt, userrec *user, CmdResult result, const std::string &original_line);
virtual void OnGetServerDescription(const std::string &servername,std::string &description);
virtual void OnUserInvite(userrec* source,userrec* dest,chanrec* channel);
virtual void OnPostLocalTopicChange(userrec* user, chanrec* chan, const std::string &topic);
virtual void OnWallops(userrec* user, const std::string &text);
virtual void OnUserNotice(userrec* user, void* dest, int target_type, const std::string &text, char status, const CUList &exempt_list);
virtual void OnUserMessage(userrec* user, void* dest, int target_type, const std::string &text, char status, const CUList &exempt_list);
virtual void OnBackgroundTimer(time_t curtime);
virtual void OnUserJoin(userrec* user, chanrec* channel, bool &silent);
virtual void OnChangeHost(userrec* user, const std::string &newhost);
virtual void OnChangeName(userrec* user, const std::string &gecos);
virtual void OnUserPart(userrec* user, chanrec* channel, const std::string &partmessage, bool &silent);
virtual void OnUserConnect(userrec* user);
virtual void OnUserQuit(userrec* user, const std::string &reason, const std::string &oper_message);
virtual void OnUserPostNick(userrec* user, const std::string &oldnick);
virtual void OnUserKick(userrec* source, userrec* user, chanrec* chan, const std::string &reason, bool &silent);
virtual void OnRemoteKill(userrec* source, userrec* dest, const std::string &reason, const std::string &operreason);
virtual void OnRehash(userrec* user, const std::string ¶meter);
virtual void OnOper(userrec* user, const std::string &opertype);
void OnLine(userrec* source, const std::string &host, bool adding, char linetype, long duration, const std::string &reason);
virtual void OnAddGLine(long duration, userrec* source, const std::string &reason, const std::string &hostmask);
virtual void OnAddZLine(long duration, userrec* source, const std::string &reason, const std::string &ipmask);
virtual void OnAddQLine(long duration, userrec* source, const std::string &reason, const std::string &nickmask);
virtual void OnAddELine(long duration, userrec* source, const std::string &reason, const std::string &hostmask);
virtual void OnDelGLine(userrec* source, const std::string &hostmask);
virtual void OnDelZLine(userrec* source, const std::string &ipmask);
virtual void OnDelQLine(userrec* source, const std::string &nickmask);
virtual void OnDelELine(userrec* source, const std::string &hostmask);
virtual void OnMode(userrec* user, void* dest, int target_type, const std::string &text);
virtual int OnStats(char statschar, userrec* user, string_list &results);
virtual void OnSetAway(userrec* user);
virtual void OnCancelAway(userrec* user);
virtual void ProtoSendMode(void* opaque, int target_type, void* target, const std::string &modeline);
virtual void ProtoSendMetaData(void* opaque, int target_type, void* target, const std::string &extname, const std::string &extdata);
virtual void OnEvent(Event* event);
virtual ~ModuleSpanningTree();
virtual Version GetVersion();
void Implements(char* List);
Priority Prioritize();
};
#endif
\ No newline at end of file +/* +------------------------------------+ + * | 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. + * + * --------------------------------------------------- + */ + +#ifndef __ST_MAIN__ +#define __ST_MAIN__ + +#include "inspircd.h" +#include "modules.h" + +/** If you make a change which breaks the protocol, increment this. + * If you completely change the protocol, completely change the number. + * + * IMPORTANT: If you make changes, document your changes here, without fail: + * http://www.inspircd.org/wiki/List_of_protocol_changes_between_versions + * + * Failure to document your protocol changes will result in a painfully + * painful death by pain. You have been warned. + */ +const long ProtocolVersion = 1105; + +/** Forward declarations + */ +class cmd_rconnect; +class cmd_rsquit; +class SpanningTreeUtilities; +class TimeSyncTimer; +class CacheRefreshTimer; +class TreeServer; +class Link; + +/** This is the main class for the spanningtree module + */ +class ModuleSpanningTree : public Module +{ + int line; + int NumServers; + unsigned int max_local; + unsigned int max_global; + cmd_rconnect* command_rconnect; + cmd_rsquit* command_rsquit; + SpanningTreeUtilities* Utils; + + public: + /** Timer for clock syncs + */ + TimeSyncTimer *SyncTimer; + + CacheRefreshTimer *RefreshTimer; + + /** Constructor + */ + ModuleSpanningTree(InspIRCd* Me); + + /** Shows /LINKS + */ + void ShowLinks(TreeServer* Current, userrec* user, int hops); + + /** Counts local servers + */ + int CountLocalServs(); + + /** Counts local and remote servers + */ + int CountServs(); + + /** Handle LINKS command + */ + void HandleLinks(const char** parameters, int pcnt, userrec* user); + + /** Handle LUSERS command + */ + void HandleLusers(const char** parameters, int pcnt, userrec* user); + + /** Show MAP output to a user (recursive) + */ + void ShowMap(TreeServer* Current, userrec* user, int depth, char matrix[128][128], float &totusers, float &totservers); + + /** Handle remote MOTD + */ + int HandleMotd(const char** parameters, int pcnt, userrec* user); + + /** Handle remote ADMIN + */ + int HandleAdmin(const char** parameters, int pcnt, userrec* user); + + /** Handle remote STATS + */ + int HandleStats(const char** parameters, int pcnt, userrec* user); + + /** Handle MAP command + */ + void HandleMap(const char** parameters, int pcnt, userrec* user); + + /** Handle SQUIT + */ + int HandleSquit(const char** parameters, int pcnt, userrec* user); + + /** Handle TIME + */ + int HandleTime(const char** parameters, int pcnt, userrec* user); + + /** Handle remote WHOIS + */ + int HandleRemoteWhois(const char** parameters, int pcnt, userrec* user); + + /** Handle remote MODULES + */ + int HandleModules(const char** parameters, int pcnt, userrec* user); + + /** Ping all local servers + */ + void DoPingChecks(time_t curtime); + + /** Connect a server locally + */ + void ConnectServer(Link* x); + + /** Check if any servers are due to be autoconnected + */ + void AutoConnectServers(time_t curtime); + + /** Handle remote VERSON + */ + int HandleVersion(const char** parameters, int pcnt, userrec* user); + + /** Handle CONNECT + */ + int HandleConnect(const char** parameters, int pcnt, userrec* user); + + /** Send out time sync to all servers + */ + void BroadcastTimeSync(); + + /** Returns oper-specific MAP information + */ + const std::string MapOperInfo(TreeServer* Current); + + /** Display a time as a human readable string + */ + std::string TimeToStr(time_t secs); + + /** + ** *** MODULE EVENTS *** + **/ + + virtual int OnPreCommand(const std::string &command, const char** parameters, int pcnt, userrec *user, bool validated, const std::string &original_line); + virtual void OnPostCommand(const std::string &command, const char** parameters, int pcnt, userrec *user, CmdResult result, const std::string &original_line); + virtual void OnGetServerDescription(const std::string &servername,std::string &description); + virtual void OnUserInvite(userrec* source,userrec* dest,chanrec* channel); + virtual void OnPostLocalTopicChange(userrec* user, chanrec* chan, const std::string &topic); + virtual void OnWallops(userrec* user, const std::string &text); + virtual void OnUserNotice(userrec* user, void* dest, int target_type, const std::string &text, char status, const CUList &exempt_list); + virtual void OnUserMessage(userrec* user, void* dest, int target_type, const std::string &text, char status, const CUList &exempt_list); + virtual void OnBackgroundTimer(time_t curtime); + virtual void OnUserJoin(userrec* user, chanrec* channel, bool &silent); + virtual void OnChangeHost(userrec* user, const std::string &newhost); + virtual void OnChangeName(userrec* user, const std::string &gecos); + virtual void OnUserPart(userrec* user, chanrec* channel, const std::string &partmessage, bool &silent); + virtual void OnUserConnect(userrec* user); + virtual void OnUserQuit(userrec* user, const std::string &reason, const std::string &oper_message); + virtual void OnUserPostNick(userrec* user, const std::string &oldnick); + virtual void OnUserKick(userrec* source, userrec* user, chanrec* chan, const std::string &reason, bool &silent); + virtual void OnRemoteKill(userrec* source, userrec* dest, const std::string &reason, const std::string &operreason); + virtual void OnRehash(userrec* user, const std::string ¶meter); + virtual void OnOper(userrec* user, const std::string &opertype); + void OnLine(userrec* source, const std::string &host, bool adding, char linetype, long duration, const std::string &reason); + virtual void OnAddGLine(long duration, userrec* source, const std::string &reason, const std::string &hostmask); + virtual void OnAddZLine(long duration, userrec* source, const std::string &reason, const std::string &ipmask); + virtual void OnAddQLine(long duration, userrec* source, const std::string &reason, const std::string &nickmask); + virtual void OnAddELine(long duration, userrec* source, const std::string &reason, const std::string &hostmask); + virtual void OnDelGLine(userrec* source, const std::string &hostmask); + virtual void OnDelZLine(userrec* source, const std::string &ipmask); + virtual void OnDelQLine(userrec* source, const std::string &nickmask); + virtual void OnDelELine(userrec* source, const std::string &hostmask); + virtual void OnMode(userrec* user, void* dest, int target_type, const std::string &text); + virtual int OnStats(char statschar, userrec* user, string_list &results); + virtual void OnSetAway(userrec* user); + virtual void OnCancelAway(userrec* user); + virtual void ProtoSendMode(void* opaque, int target_type, void* target, const std::string &modeline); + virtual void ProtoSendMetaData(void* opaque, int target_type, void* target, const std::string &extname, const std::string &extdata); + virtual void OnEvent(Event* event); + virtual ~ModuleSpanningTree(); + virtual Version GetVersion(); + void Implements(char* List); + Priority Prioritize(); +}; + +#endif diff --git a/src/modules/m_spanningtree/rconnect.cpp b/src/modules/m_spanningtree/rconnect.cpp index 88b1fde8b..5500ccdc0 100644 --- a/src/modules/m_spanningtree/rconnect.cpp +++ b/src/modules/m_spanningtree/rconnect.cpp @@ -1 +1,67 @@ -/* +------------------------------------+
* | 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 "users.h"
#include "channels.h"
#include "modules.h"
#include "commands/cmd_whois.h"
#include "commands/cmd_stats.h"
#include "socket.h"
#include "wildcard.h"
#include "xline.h"
#include "transport.h"
#include "m_spanningtree/timesynctimer.h"
#include "m_spanningtree/resolvers.h"
#include "m_spanningtree/main.h"
#include "m_spanningtree/utils.h"
#include "m_spanningtree/treeserver.h"
#include "m_spanningtree/link.h"
#include "m_spanningtree/treesocket.h"
#include "m_spanningtree/rconnect.h"
/* $ModDep: m_spanningtree/timesynctimer.h m_spanningtree/resolvers.h m_spanningtree/main.h m_spanningtree/utils.h m_spanningtree/treeserver.h m_spanningtree/link.h m_spanningtree/treesocket.h m_spanningtree/rconnect.h */
cmd_rconnect::cmd_rconnect (InspIRCd* Instance, Module* Callback, SpanningTreeUtilities* Util) : command_t(Instance, "RCONNECT", 'o', 2), Creator(Callback), Utils(Util)
{
this->source = "m_spanningtree.so";
syntax = "<remote-server-mask> <target-server-mask>";
}
CmdResult cmd_rconnect::Handle (const char** parameters, int pcnt, userrec *user)
{
if (IS_LOCAL(user))
{
if (!Utils->FindServerMask(parameters[0]))
{
user->WriteServ("NOTICE %s :*** RCONNECT: Server \002%s\002 isn't connected to the network!", user->nick, parameters[0]);
return CMD_FAILURE;
}
user->WriteServ("NOTICE %s :*** RCONNECT: Sending remote connect to \002%s\002 to connect server \002%s\002.",user->nick,parameters[0],parameters[1]);
}
/* Is this aimed at our server? */
if (ServerInstance->MatchText(ServerInstance->Config->ServerName,parameters[0]))
{
/* Yes, initiate the given connect */
ServerInstance->SNO->WriteToSnoMask('l',"Remote CONNECT from %s matching \002%s\002, connecting server \002%s\002",user->nick,parameters[0],parameters[1]);
const char* para[1];
para[0] = parameters[1];
std::string original_command = std::string("CONNECT ") + parameters[1];
Creator->OnPreCommand("CONNECT", para, 1, user, true, original_command);
}
return CMD_SUCCESS;
}
\ No newline at end of file +/* +------------------------------------+ + * | 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 "users.h" +#include "channels.h" +#include "modules.h" +#include "commands/cmd_whois.h" +#include "commands/cmd_stats.h" +#include "socket.h" +#include "wildcard.h" +#include "xline.h" +#include "transport.h" + +#include "m_spanningtree/timesynctimer.h" +#include "m_spanningtree/resolvers.h" +#include "m_spanningtree/main.h" +#include "m_spanningtree/utils.h" +#include "m_spanningtree/treeserver.h" +#include "m_spanningtree/link.h" +#include "m_spanningtree/treesocket.h" +#include "m_spanningtree/rconnect.h" + +/* $ModDep: m_spanningtree/timesynctimer.h m_spanningtree/resolvers.h m_spanningtree/main.h m_spanningtree/utils.h m_spanningtree/treeserver.h m_spanningtree/link.h m_spanningtree/treesocket.h m_spanningtree/rconnect.h */ + +cmd_rconnect::cmd_rconnect (InspIRCd* Instance, Module* Callback, SpanningTreeUtilities* Util) : command_t(Instance, "RCONNECT", 'o', 2), Creator(Callback), Utils(Util) +{ + this->source = "m_spanningtree.so"; + syntax = "<remote-server-mask> <target-server-mask>"; +} + +CmdResult cmd_rconnect::Handle (const char** parameters, int pcnt, userrec *user) +{ + if (IS_LOCAL(user)) + { + if (!Utils->FindServerMask(parameters[0])) + { + user->WriteServ("NOTICE %s :*** RCONNECT: Server \002%s\002 isn't connected to the network!", user->nick, parameters[0]); + return CMD_FAILURE; + } + user->WriteServ("NOTICE %s :*** RCONNECT: Sending remote connect to \002%s\002 to connect server \002%s\002.",user->nick,parameters[0],parameters[1]); + } + + /* Is this aimed at our server? */ + if (ServerInstance->MatchText(ServerInstance->Config->ServerName,parameters[0])) + { + /* Yes, initiate the given connect */ + ServerInstance->SNO->WriteToSnoMask('l',"Remote CONNECT from %s matching \002%s\002, connecting server \002%s\002",user->nick,parameters[0],parameters[1]); + const char* para[1]; + para[0] = parameters[1]; + std::string original_command = std::string("CONNECT ") + parameters[1]; + Creator->OnPreCommand("CONNECT", para, 1, user, true, original_command); + } + return CMD_SUCCESS; +} + diff --git a/src/modules/m_spanningtree/rconnect.h b/src/modules/m_spanningtree/rconnect.h index fca96f4a8..77e271949 100644 --- a/src/modules/m_spanningtree/rconnect.h +++ b/src/modules/m_spanningtree/rconnect.h @@ -1 +1,28 @@ -/* +------------------------------------+
* | 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.
*
* ---------------------------------------------------
*/
#ifndef __RCONNECT_H__
#define __RCONNECT_H__
/** Handle /RCONNECT
*/
class cmd_rconnect : public command_t
{
Module* Creator; /* Creator */
SpanningTreeUtilities* Utils; /* Utility class */
public:
cmd_rconnect (InspIRCd* Instance, Module* Callback, SpanningTreeUtilities* Util);
CmdResult Handle (const char** parameters, int pcnt, userrec *user);
};
#endif
\ No newline at end of file +/* +------------------------------------+ + * | 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. + * + * --------------------------------------------------- + */ + +#ifndef __RCONNECT_H__ +#define __RCONNECT_H__ + +/** Handle /RCONNECT + */ +class cmd_rconnect : public command_t +{ + Module* Creator; /* Creator */ + SpanningTreeUtilities* Utils; /* Utility class */ + public: + cmd_rconnect (InspIRCd* Instance, Module* Callback, SpanningTreeUtilities* Util); + CmdResult Handle (const char** parameters, int pcnt, userrec *user); +}; + +#endif diff --git a/src/modules/m_spanningtree/resolvers.cpp b/src/modules/m_spanningtree/resolvers.cpp index 80971c699..0d94da99f 100644 --- a/src/modules/m_spanningtree/resolvers.cpp +++ b/src/modules/m_spanningtree/resolvers.cpp @@ -1 +1,88 @@ -/* +------------------------------------+
* | 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 "users.h"
#include "channels.h"
#include "modules.h"
#include "commands/cmd_whois.h"
#include "commands/cmd_stats.h"
#include "socket.h"
#include "wildcard.h"
#include "xline.h"
#include "transport.h"
#include "m_spanningtree/resolvers.h"
#include "m_spanningtree/main.h"
#include "m_spanningtree/utils.h"
#include "m_spanningtree/treeserver.h"
#include "m_spanningtree/link.h"
#include "m_spanningtree/treesocket.h"
/* $ModDep: m_spanningtree/timesynctimer.h m_spanningtree/resolvers.h m_spanningtree/main.h m_spanningtree/utils.h m_spanningtree/treeserver.h m_spanningtree/link.h m_spanningtree/treesocket.h */
/** This class is used to resolve server hostnames during /connect and autoconnect.
* As of 1.1, the resolver system is seperated out from InspSocket, so we must do this
* resolver step first ourselves if we need it. This is totally nonblocking, and will
* callback to OnLookupComplete or OnError when completed. Once it has completed we
* will have an IP address which we can then use to continue our connection.
*/
ServernameResolver::ServernameResolver(Module* me, SpanningTreeUtilities* Util, InspIRCd* Instance, const std::string &hostname, Link x, bool &cached, QueryType qt) : Resolver(Instance, hostname, qt, cached, me), MyLink(x), Utils(Util), query(qt), host(hostname), mine(me)
{
/* Nothing in here, folks */
}
void ServernameResolver::OnLookupComplete(const std::string &result, unsigned int ttl, bool cached)
{
/* Initiate the connection, now that we have an IP to use.
* Passing a hostname directly to InspSocket causes it to
* just bail and set its FD to -1.
*/
TreeServer* CheckDupe = Utils->FindServer(MyLink.Name.c_str());
if (!CheckDupe) /* Check that nobody tried to connect it successfully while we were resolving */
{
if ((!MyLink.Hook.empty()) && (Utils->hooks.find(MyLink.Hook.c_str()) == Utils->hooks.end()))
return;
TreeSocket* newsocket = new TreeSocket(this->Utils, ServerInstance, result,MyLink.Port,false,MyLink.Timeout ? MyLink.Timeout : 10,MyLink.Name.c_str(),
MyLink.Bind, MyLink.Hook.empty() ? NULL : Utils->hooks[MyLink.Hook.c_str()]);
if (newsocket->GetFd() > -1)
{
/* We're all OK */
}
else
{
/* Something barfed, show the opers */
ServerInstance->SNO->WriteToSnoMask('l',"CONNECT: Error connecting \002%s\002: %s.",MyLink.Name.c_str(),strerror(errno));
delete newsocket;
Utils->DoFailOver(&MyLink);
}
}
}
void ServernameResolver::OnError(ResolverError e, const std::string &errormessage)
{
/* Ooops! */
if (query == DNS_QUERY_AAAA)
{
bool cached;
ServernameResolver* snr = new ServernameResolver(mine, Utils, ServerInstance, host, MyLink, cached, DNS_QUERY_A);
ServerInstance->AddResolver(snr, cached);
return;
}
ServerInstance->SNO->WriteToSnoMask('l',"CONNECT: Error connecting \002%s\002: Unable to resolve hostname - %s",MyLink.Name.c_str(),errormessage.c_str());
Utils->DoFailOver(&MyLink);
}
\ No newline at end of file +/* +------------------------------------+ + * | 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 "users.h" +#include "channels.h" +#include "modules.h" +#include "commands/cmd_whois.h" +#include "commands/cmd_stats.h" +#include "socket.h" +#include "wildcard.h" +#include "xline.h" +#include "transport.h" + +#include "m_spanningtree/resolvers.h" +#include "m_spanningtree/main.h" +#include "m_spanningtree/utils.h" +#include "m_spanningtree/treeserver.h" +#include "m_spanningtree/link.h" +#include "m_spanningtree/treesocket.h" + +/* $ModDep: m_spanningtree/timesynctimer.h m_spanningtree/resolvers.h m_spanningtree/main.h m_spanningtree/utils.h m_spanningtree/treeserver.h m_spanningtree/link.h m_spanningtree/treesocket.h */ + +/** This class is used to resolve server hostnames during /connect and autoconnect. + * As of 1.1, the resolver system is seperated out from InspSocket, so we must do this + * resolver step first ourselves if we need it. This is totally nonblocking, and will + * callback to OnLookupComplete or OnError when completed. Once it has completed we + * will have an IP address which we can then use to continue our connection. + */ +ServernameResolver::ServernameResolver(Module* me, SpanningTreeUtilities* Util, InspIRCd* Instance, const std::string &hostname, Link x, bool &cached, QueryType qt) : Resolver(Instance, hostname, qt, cached, me), MyLink(x), Utils(Util), query(qt), host(hostname), mine(me) +{ + /* Nothing in here, folks */ +} + +void ServernameResolver::OnLookupComplete(const std::string &result, unsigned int ttl, bool cached) +{ + /* Initiate the connection, now that we have an IP to use. + * Passing a hostname directly to InspSocket causes it to + * just bail and set its FD to -1. + */ + TreeServer* CheckDupe = Utils->FindServer(MyLink.Name.c_str()); + if (!CheckDupe) /* Check that nobody tried to connect it successfully while we were resolving */ + { + + if ((!MyLink.Hook.empty()) && (Utils->hooks.find(MyLink.Hook.c_str()) == Utils->hooks.end())) + return; + + TreeSocket* newsocket = new TreeSocket(this->Utils, ServerInstance, result,MyLink.Port,false,MyLink.Timeout ? MyLink.Timeout : 10,MyLink.Name.c_str(), + MyLink.Bind, MyLink.Hook.empty() ? NULL : Utils->hooks[MyLink.Hook.c_str()]); + if (newsocket->GetFd() > -1) + { + /* We're all OK */ + } + else + { + /* Something barfed, show the opers */ + ServerInstance->SNO->WriteToSnoMask('l',"CONNECT: Error connecting \002%s\002: %s.",MyLink.Name.c_str(),strerror(errno)); + delete newsocket; + Utils->DoFailOver(&MyLink); + } + } +} + +void ServernameResolver::OnError(ResolverError e, const std::string &errormessage) +{ + /* Ooops! */ + if (query == DNS_QUERY_AAAA) + { + bool cached; + ServernameResolver* snr = new ServernameResolver(mine, Utils, ServerInstance, host, MyLink, cached, DNS_QUERY_A); + ServerInstance->AddResolver(snr, cached); + return; + } + ServerInstance->SNO->WriteToSnoMask('l',"CONNECT: Error connecting \002%s\002: Unable to resolve hostname - %s",MyLink.Name.c_str(),errormessage.c_str()); + Utils->DoFailOver(&MyLink); +} + diff --git a/src/modules/m_spanningtree/resolvers.h b/src/modules/m_spanningtree/resolvers.h index 0ba9d6bd6..06fd05bad 100644 --- a/src/modules/m_spanningtree/resolvers.h +++ b/src/modules/m_spanningtree/resolvers.h @@ -1 +1,90 @@ -/* +------------------------------------+
* | 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.
*
* ---------------------------------------------------
*/
#ifndef __RESOLVERS__H__
#define __RESOLVERS__H__
#include "configreader.h"
#include "users.h"
#include "channels.h"
#include "modules.h"
#include "commands/cmd_whois.h"
#include "commands/cmd_stats.h"
#include "socket.h"
#include "inspircd.h"
#include "wildcard.h"
#include "xline.h"
#include "transport.h"
#include "m_spanningtree/utils.h"
#include "m_spanningtree/link.h"
/** Handle resolving of server IPs for the cache
*/
class SecurityIPResolver : public Resolver
{
private:
Link MyLink;
SpanningTreeUtilities* Utils;
Module* mine;
std::string host;
QueryType query;
public:
SecurityIPResolver(Module* me, SpanningTreeUtilities* U, InspIRCd* Instance, const std::string &hostname, Link x, bool &cached, QueryType qt)
: Resolver(Instance, hostname, qt, cached, me), MyLink(x), Utils(U), mine(me), host(hostname), query(qt)
{
}
void OnLookupComplete(const std::string &result, unsigned int ttl, bool cached)
{
Utils->ValidIPs.push_back(result);
}
void OnError(ResolverError e, const std::string &errormessage)
{
if (query == DNS_QUERY_AAAA)
{
bool cached;
SecurityIPResolver* res = new SecurityIPResolver(mine, Utils, ServerInstance, host, MyLink, cached, DNS_QUERY_A);
ServerInstance->AddResolver(res, cached);
return;
}
ServerInstance->Log(DEFAULT,"Could not resolve IP associated with Link '%s': %s",MyLink.Name.c_str(),errormessage.c_str());
}
};
/** This class is used to resolve server hostnames during /connect and autoconnect.
* As of 1.1, the resolver system is seperated out from InspSocket, so we must do this
* resolver step first ourselves if we need it. This is totally nonblocking, and will
* callback to OnLookupComplete or OnError when completed. Once it has completed we
* will have an IP address which we can then use to continue our connection.
*/
class ServernameResolver : public Resolver
{
private:
/** A copy of the Link tag info for what we're connecting to.
* We take a copy, rather than using a pointer, just in case the
* admin takes the tag away and rehashes while the domain is resolving.
*/
Link MyLink;
SpanningTreeUtilities* Utils;
QueryType query;
std::string host;
Module* mine;
public:
ServernameResolver(Module* me, SpanningTreeUtilities* Util, InspIRCd* Instance, const std::string &hostname, Link x, bool &cached, QueryType qt);
void OnLookupComplete(const std::string &result, unsigned int ttl, bool cached);
void OnError(ResolverError e, const std::string &errormessage);
};
#endif
\ No newline at end of file +/* +------------------------------------+ + * | 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. + * + * --------------------------------------------------- + */ + +#ifndef __RESOLVERS__H__ +#define __RESOLVERS__H__ + +#include "configreader.h" +#include "users.h" +#include "channels.h" +#include "modules.h" +#include "commands/cmd_whois.h" +#include "commands/cmd_stats.h" +#include "socket.h" +#include "inspircd.h" +#include "wildcard.h" +#include "xline.h" +#include "transport.h" + +#include "m_spanningtree/utils.h" +#include "m_spanningtree/link.h" + +/** Handle resolving of server IPs for the cache + */ +class SecurityIPResolver : public Resolver +{ + private: + Link MyLink; + SpanningTreeUtilities* Utils; + Module* mine; + std::string host; + QueryType query; + public: + SecurityIPResolver(Module* me, SpanningTreeUtilities* U, InspIRCd* Instance, const std::string &hostname, Link x, bool &cached, QueryType qt) + : Resolver(Instance, hostname, qt, cached, me), MyLink(x), Utils(U), mine(me), host(hostname), query(qt) + { + } + + void OnLookupComplete(const std::string &result, unsigned int ttl, bool cached) + { + Utils->ValidIPs.push_back(result); + } + + void OnError(ResolverError e, const std::string &errormessage) + { + if (query == DNS_QUERY_AAAA) + { + bool cached; + SecurityIPResolver* res = new SecurityIPResolver(mine, Utils, ServerInstance, host, MyLink, cached, DNS_QUERY_A); + ServerInstance->AddResolver(res, cached); + return; + } + ServerInstance->Log(DEFAULT,"Could not resolve IP associated with Link '%s': %s",MyLink.Name.c_str(),errormessage.c_str()); + } +}; + +/** This class is used to resolve server hostnames during /connect and autoconnect. + * As of 1.1, the resolver system is seperated out from InspSocket, so we must do this + * resolver step first ourselves if we need it. This is totally nonblocking, and will + * callback to OnLookupComplete or OnError when completed. Once it has completed we + * will have an IP address which we can then use to continue our connection. + */ +class ServernameResolver : public Resolver +{ + private: + /** A copy of the Link tag info for what we're connecting to. + * We take a copy, rather than using a pointer, just in case the + * admin takes the tag away and rehashes while the domain is resolving. + */ + Link MyLink; + SpanningTreeUtilities* Utils; + QueryType query; + std::string host; + Module* mine; + public: + ServernameResolver(Module* me, SpanningTreeUtilities* Util, InspIRCd* Instance, const std::string &hostname, Link x, bool &cached, QueryType qt); + void OnLookupComplete(const std::string &result, unsigned int ttl, bool cached); + void OnError(ResolverError e, const std::string &errormessage); +}; + +#endif diff --git a/src/modules/m_spanningtree/rsquit.cpp b/src/modules/m_spanningtree/rsquit.cpp index 7bb6abfc1..5f3d33fc0 100644 --- a/src/modules/m_spanningtree/rsquit.cpp +++ b/src/modules/m_spanningtree/rsquit.cpp @@ -1 +1,123 @@ -/* +------------------------------------+
* | 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 "users.h"
#include "channels.h"
#include "modules.h"
#include "commands/cmd_whois.h"
#include "commands/cmd_stats.h"
#include "socket.h"
#include "wildcard.h"
#include "xline.h"
#include "transport.h"
#include "m_spanningtree/timesynctimer.h"
#include "m_spanningtree/resolvers.h"
#include "m_spanningtree/main.h"
#include "m_spanningtree/utils.h"
#include "m_spanningtree/treeserver.h"
#include "m_spanningtree/link.h"
#include "m_spanningtree/treesocket.h"
#include "m_spanningtree/rsquit.h"
/* $ModDep: m_spanningtree/timesynctimer.h m_spanningtree/resolvers.h m_spanningtree/main.h m_spanningtree/utils.h m_spanningtree/treeserver.h m_spanningtree/link.h m_spanningtree/treesocket.h m_spanningtree/rsquit.h */
cmd_rsquit::cmd_rsquit (InspIRCd* Instance, Module* Callback, SpanningTreeUtilities* Util) : command_t(Instance, "RSQUIT", 'o', 1), Creator(Callback), Utils(Util)
{
this->source = "m_spanningtree.so";
syntax = "<remote-server-mask> [target-server-mask]";
}
CmdResult cmd_rsquit::Handle (const char** parameters, int pcnt, userrec *user)
{
if (IS_LOCAL(user))
{
if (!Utils->FindServerMask(parameters[0]))
{
user->WriteServ("NOTICE %s :*** RSQUIT: Server \002%s\002 isn't connected to the network!", user->nick, parameters[0]);
return CMD_FAILURE;
}
if (pcnt > 1)
user->WriteServ("NOTICE %s :*** RSQUIT: Sending remote squit to \002%s\002 to squit server \002%s\002.",user->nick,parameters[0],parameters[1]);
else
user->WriteServ("NOTICE %s :*** RSQUIT: Sending remote squit for server \002%s\002.",user->nick,parameters[0]);
}
TreeServer* s = (pcnt > 1) ? Utils->FindServerMask(parameters[1]) : Utils->FindServerMask(parameters[0]);
if (pcnt > 1)
{
if (ServerInstance->MatchText(ServerInstance->Config->ServerName,parameters[0]))
{
if (s)
{
if (s == Utils->TreeRoot)
{
NoticeUser(user, "*** RSQUIT: Foolish mortal, you cannot make a server SQUIT itself! ("+ConvToStr(parameters[1])+" matches local server name)");
return CMD_FAILURE;
}
TreeSocket* sock = s->GetSocket();
if (!sock)
{
NoticeUser(user, "*** RSQUIT: Server \002"+ConvToStr(parameters[1])+"\002 isn't connected to \002"+ConvToStr(parameters[0])+"\002.");
return CMD_FAILURE;
}
ServerInstance->SNO->WriteToSnoMask('l',"Remote SQUIT from %s matching \002%s\002, squitting server \002%s\002",user->nick,parameters[0],parameters[1]);
const char* para[1];
para[0] = parameters[1];
std::string original_command = std::string("SQUIT ") + parameters[1];
Creator->OnPreCommand("SQUIT", para, 1, user, true, original_command);
return CMD_LOCALONLY;
}
}
}
else
{
if (s)
{
if (s == Utils->TreeRoot)
{
NoticeUser(user, "*** RSQUIT: Foolish mortal, you cannot make a server SQUIT itself! ("+ConvToStr(parameters[0])+" matches local server name)");
return CMD_FAILURE;
}
TreeSocket* sock = s->GetSocket();
if (sock)
{
ServerInstance->SNO->WriteToSnoMask('l',"RSQUIT: Server \002%s\002 removed from network by %s",parameters[0],user->nick);
sock->Squit(s,std::string("Server quit by ") + user->GetFullRealHost());
ServerInstance->SE->DelFd(sock);
sock->Close();
return CMD_LOCALONLY;
}
}
}
return CMD_SUCCESS;
}
void cmd_rsquit::NoticeUser(userrec* user, const std::string &msg)
{
if (IS_LOCAL(user))
{
user->WriteServ("NOTICE %s :%s",user->nick,msg.c_str());
}
else
{
std::deque<std::string> params;
params.push_back(user->nick);
params.push_back("NOTICE "+ConvToStr(user->nick)+" :"+msg);
Utils->DoOneToOne(ServerInstance->Config->ServerName, "PUSH", params, user->server);
}
}
\ No newline at end of file +/* +------------------------------------+ + * | 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 "users.h" +#include "channels.h" +#include "modules.h" +#include "commands/cmd_whois.h" +#include "commands/cmd_stats.h" +#include "socket.h" +#include "wildcard.h" +#include "xline.h" +#include "transport.h" + +#include "m_spanningtree/timesynctimer.h" +#include "m_spanningtree/resolvers.h" +#include "m_spanningtree/main.h" +#include "m_spanningtree/utils.h" +#include "m_spanningtree/treeserver.h" +#include "m_spanningtree/link.h" +#include "m_spanningtree/treesocket.h" +#include "m_spanningtree/rsquit.h" + +/* $ModDep: m_spanningtree/timesynctimer.h m_spanningtree/resolvers.h m_spanningtree/main.h m_spanningtree/utils.h m_spanningtree/treeserver.h m_spanningtree/link.h m_spanningtree/treesocket.h m_spanningtree/rsquit.h */ + +cmd_rsquit::cmd_rsquit (InspIRCd* Instance, Module* Callback, SpanningTreeUtilities* Util) : command_t(Instance, "RSQUIT", 'o', 1), Creator(Callback), Utils(Util) +{ + this->source = "m_spanningtree.so"; + syntax = "<remote-server-mask> [target-server-mask]"; +} + +CmdResult cmd_rsquit::Handle (const char** parameters, int pcnt, userrec *user) +{ + if (IS_LOCAL(user)) + { + if (!Utils->FindServerMask(parameters[0])) + { + user->WriteServ("NOTICE %s :*** RSQUIT: Server \002%s\002 isn't connected to the network!", user->nick, parameters[0]); + return CMD_FAILURE; + } + if (pcnt > 1) + user->WriteServ("NOTICE %s :*** RSQUIT: Sending remote squit to \002%s\002 to squit server \002%s\002.",user->nick,parameters[0],parameters[1]); + else + user->WriteServ("NOTICE %s :*** RSQUIT: Sending remote squit for server \002%s\002.",user->nick,parameters[0]); + } + + TreeServer* s = (pcnt > 1) ? Utils->FindServerMask(parameters[1]) : Utils->FindServerMask(parameters[0]); + + if (pcnt > 1) + { + if (ServerInstance->MatchText(ServerInstance->Config->ServerName,parameters[0])) + { + if (s) + { + if (s == Utils->TreeRoot) + { + NoticeUser(user, "*** RSQUIT: Foolish mortal, you cannot make a server SQUIT itself! ("+ConvToStr(parameters[1])+" matches local server name)"); + return CMD_FAILURE; + } + TreeSocket* sock = s->GetSocket(); + if (!sock) + { + NoticeUser(user, "*** RSQUIT: Server \002"+ConvToStr(parameters[1])+"\002 isn't connected to \002"+ConvToStr(parameters[0])+"\002."); + return CMD_FAILURE; + } + ServerInstance->SNO->WriteToSnoMask('l',"Remote SQUIT from %s matching \002%s\002, squitting server \002%s\002",user->nick,parameters[0],parameters[1]); + const char* para[1]; + para[0] = parameters[1]; + std::string original_command = std::string("SQUIT ") + parameters[1]; + Creator->OnPreCommand("SQUIT", para, 1, user, true, original_command); + return CMD_LOCALONLY; + } + } + } + else + { + if (s) + { + if (s == Utils->TreeRoot) + { + NoticeUser(user, "*** RSQUIT: Foolish mortal, you cannot make a server SQUIT itself! ("+ConvToStr(parameters[0])+" matches local server name)"); + return CMD_FAILURE; + } + TreeSocket* sock = s->GetSocket(); + if (sock) + { + ServerInstance->SNO->WriteToSnoMask('l',"RSQUIT: Server \002%s\002 removed from network by %s",parameters[0],user->nick); + sock->Squit(s,std::string("Server quit by ") + user->GetFullRealHost()); + ServerInstance->SE->DelFd(sock); + sock->Close(); + return CMD_LOCALONLY; + } + } + } + + return CMD_SUCCESS; +} + +void cmd_rsquit::NoticeUser(userrec* user, const std::string &msg) +{ + if (IS_LOCAL(user)) + { + user->WriteServ("NOTICE %s :%s",user->nick,msg.c_str()); + } + else + { + std::deque<std::string> params; + params.push_back(user->nick); + params.push_back("NOTICE "+ConvToStr(user->nick)+" :"+msg); + Utils->DoOneToOne(ServerInstance->Config->ServerName, "PUSH", params, user->server); + } +} diff --git a/src/modules/m_spanningtree/rsquit.h b/src/modules/m_spanningtree/rsquit.h index ed9eb83d4..81e9bc2b7 100644 --- a/src/modules/m_spanningtree/rsquit.h +++ b/src/modules/m_spanningtree/rsquit.h @@ -1 +1,29 @@ -/* +------------------------------------+
* | 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.
*
* ---------------------------------------------------
*/
#ifndef __RSQUIT_H__
#define __RSQUIT_H__
/** Handle /RCONNECT
*/
class cmd_rsquit : public command_t
{
Module* Creator; /* Creator */
SpanningTreeUtilities* Utils; /* Utility class */
public:
cmd_rsquit (InspIRCd* Instance, Module* Callback, SpanningTreeUtilities* Util);
CmdResult Handle (const char** parameters, int pcnt, userrec *user);
void NoticeUser(userrec* user, const std::string &msg);
};
#endif
\ No newline at end of file +/* +------------------------------------+ + * | 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. + * + * --------------------------------------------------- + */ + +#ifndef __RSQUIT_H__ +#define __RSQUIT_H__ + +/** Handle /RCONNECT + */ +class cmd_rsquit : public command_t +{ + Module* Creator; /* Creator */ + SpanningTreeUtilities* Utils; /* Utility class */ + public: + cmd_rsquit (InspIRCd* Instance, Module* Callback, SpanningTreeUtilities* Util); + CmdResult Handle (const char** parameters, int pcnt, userrec *user); + void NoticeUser(userrec* user, const std::string &msg); +}; + +#endif diff --git a/src/modules/m_spanningtree/timesynctimer.cpp b/src/modules/m_spanningtree/timesynctimer.cpp index 8ecb84a4b..af615e91e 100644 --- a/src/modules/m_spanningtree/timesynctimer.cpp +++ b/src/modules/m_spanningtree/timesynctimer.cpp @@ -1 +1,52 @@ -/* +------------------------------------+
* | 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 "users.h"
#include "channels.h"
#include "modules.h"
#include "commands/cmd_whois.h"
#include "commands/cmd_stats.h"
#include "socket.h"
#include "wildcard.h"
#include "xline.h"
#include "transport.h"
#include "m_spanningtree/timesynctimer.h"
#include "m_spanningtree/main.h"
#include "m_spanningtree/utils.h"
#include "m_spanningtree/treeserver.h"
#include "m_spanningtree/link.h"
#include "m_spanningtree/treesocket.h"
/* $ModDep: m_spanningtree/timesynctimer.h m_spanningtree/resolvers.h m_spanningtree/main.h m_spanningtree/utils.h m_spanningtree/treeserver.h m_spanningtree/link.h m_spanningtree/treesocket.h */
TimeSyncTimer::TimeSyncTimer(InspIRCd *Inst, ModuleSpanningTree *Mod) : InspTimer(600, Inst->Time(), true), Instance(Inst), Module(Mod)
{
}
void TimeSyncTimer::Tick(time_t TIME)
{
Module->BroadcastTimeSync();
}
CacheRefreshTimer::CacheRefreshTimer(InspIRCd *Inst, SpanningTreeUtilities *Util) : InspTimer(3600, Inst->Time(), true), Instance(Inst), Utils(Util)
{
}
void CacheRefreshTimer::Tick(time_t TIME)
{
Utils->RefreshIPCache();
}
\ No newline at end of file +/* +------------------------------------+ + * | 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 "users.h" +#include "channels.h" +#include "modules.h" +#include "commands/cmd_whois.h" +#include "commands/cmd_stats.h" +#include "socket.h" +#include "wildcard.h" +#include "xline.h" +#include "transport.h" + +#include "m_spanningtree/timesynctimer.h" +#include "m_spanningtree/main.h" +#include "m_spanningtree/utils.h" +#include "m_spanningtree/treeserver.h" +#include "m_spanningtree/link.h" +#include "m_spanningtree/treesocket.h" + +/* $ModDep: m_spanningtree/timesynctimer.h m_spanningtree/resolvers.h m_spanningtree/main.h m_spanningtree/utils.h m_spanningtree/treeserver.h m_spanningtree/link.h m_spanningtree/treesocket.h */ + +TimeSyncTimer::TimeSyncTimer(InspIRCd *Inst, ModuleSpanningTree *Mod) : InspTimer(600, Inst->Time(), true), Instance(Inst), Module(Mod) +{ +} + +void TimeSyncTimer::Tick(time_t TIME) +{ + Module->BroadcastTimeSync(); +} + +CacheRefreshTimer::CacheRefreshTimer(InspIRCd *Inst, SpanningTreeUtilities *Util) : InspTimer(3600, Inst->Time(), true), Instance(Inst), Utils(Util) +{ +} + +void CacheRefreshTimer::Tick(time_t TIME) +{ + Utils->RefreshIPCache(); +} + diff --git a/src/modules/m_spanningtree/timesynctimer.h b/src/modules/m_spanningtree/timesynctimer.h index dd23ee171..434ee253c 100644 --- a/src/modules/m_spanningtree/timesynctimer.h +++ b/src/modules/m_spanningtree/timesynctimer.h @@ -1 +1,47 @@ -/* +------------------------------------+
* | 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.
*
* ---------------------------------------------------
*/
#ifndef __TIMESYNC_H__
#define __TIMESYNC_H__
#include "timer.h"
class ModuleSpanningTree;
class SpanningTreeUtilities;
class InspIRCd;
/** Create a timer which recurs every second, we inherit from InspTimer.
* InspTimer is only one-shot however, so at the end of each Tick() we simply
* insert another of ourselves into the pending queue :)
*/
class TimeSyncTimer : public InspTimer
{
private:
InspIRCd *Instance;
ModuleSpanningTree *Module;
public:
TimeSyncTimer(InspIRCd *Instance, ModuleSpanningTree *Mod);
virtual void Tick(time_t TIME);
};
class CacheRefreshTimer : public InspTimer
{
private:
InspIRCd *Instance;
SpanningTreeUtilities *Utils;
public:
CacheRefreshTimer(InspIRCd *Instance, SpanningTreeUtilities* Util);
virtual void Tick(time_t TIME);
};
#endif
\ No newline at end of file +/* +------------------------------------+ + * | 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. + * + * --------------------------------------------------- + */ + +#ifndef __TIMESYNC_H__ +#define __TIMESYNC_H__ + +#include "timer.h" + +class ModuleSpanningTree; +class SpanningTreeUtilities; +class InspIRCd; + +/** Create a timer which recurs every second, we inherit from InspTimer. + * InspTimer is only one-shot however, so at the end of each Tick() we simply + * insert another of ourselves into the pending queue :) + */ +class TimeSyncTimer : public InspTimer +{ + private: + InspIRCd *Instance; + ModuleSpanningTree *Module; + public: + TimeSyncTimer(InspIRCd *Instance, ModuleSpanningTree *Mod); + virtual void Tick(time_t TIME); +}; + +class CacheRefreshTimer : public InspTimer +{ + private: + InspIRCd *Instance; + SpanningTreeUtilities *Utils; + public: + CacheRefreshTimer(InspIRCd *Instance, SpanningTreeUtilities* Util); + virtual void Tick(time_t TIME); +}; + +#endif diff --git a/src/modules/m_spanningtree/treeserver.cpp b/src/modules/m_spanningtree/treeserver.cpp index 670f7e420..b5cac1802 100644 --- a/src/modules/m_spanningtree/treeserver.cpp +++ b/src/modules/m_spanningtree/treeserver.cpp @@ -1 +1,325 @@ -/* +------------------------------------+
* | 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 "users.h"
#include "channels.h"
#include "modules.h"
#include "commands/cmd_whois.h"
#include "commands/cmd_stats.h"
#include "socket.h"
#include "wildcard.h"
#include "xline.h"
#include "transport.h"
#include "m_spanningtree/utils.h"
#include "m_spanningtree/treeserver.h"
/* $ModDep: m_spanningtree/utils.h m_spanningtree/treeserver.h */
TreeServer::TreeServer(SpanningTreeUtilities* Util, InspIRCd* Instance) : ServerInstance(Instance), Utils(Util)
{
Parent = NULL;
ServerName.clear();
ServerDesc.clear();
VersionString.clear();
UserCount = OperCount = 0;
rtt = LastPing = 0;
Hidden = false;
VersionString = ServerInstance->GetVersionString();
}
/** We use this constructor only to create the 'root' item, Utils->TreeRoot, which
* represents our own server. Therefore, it has no route, no parent, and
* no socket associated with it. Its version string is our own local version.
*/
TreeServer::TreeServer(SpanningTreeUtilities* Util, InspIRCd* Instance, std::string Name, std::string Desc) : ServerInstance(Instance), ServerName(Name.c_str()), ServerDesc(Desc), Utils(Util)
{
Parent = NULL;
VersionString.clear();
UserCount = ServerInstance->UserCount();
OperCount = ServerInstance->OperCount();
VersionString = ServerInstance->GetVersionString();
Route = NULL;
Socket = NULL; /* Fix by brain */
rtt = LastPing = 0;
Hidden = false;
AddHashEntry();
}
/** When we create a new server, we call this constructor to initialize it.
* This constructor initializes the server's Route and Parent, and sets up
* its ping counters so that it will be pinged one minute from now.
*/
TreeServer::TreeServer(SpanningTreeUtilities* Util, InspIRCd* Instance, std::string Name, std::string Desc, TreeServer* Above, TreeSocket* Sock, bool Hide)
: ServerInstance(Instance), Parent(Above), ServerName(Name.c_str()), ServerDesc(Desc), Socket(Sock), Utils(Util), Hidden(Hide)
{
VersionString.clear();
UserCount = OperCount = 0;
this->SetNextPingTime(time(NULL) + 60);
this->SetPingFlag();
rtt = LastPing = 0;
/* find the 'route' for this server (e.g. the one directly connected
* to the local server, which we can use to reach it)
*
* In the following example, consider we have just added a TreeServer
* class for server G on our network, of which we are server A.
* To route traffic to G (marked with a *) we must send the data to
* B (marked with a +) so this algorithm initializes the 'Route'
* value to point at whichever server traffic must be routed through
* to get here. If we were to try this algorithm with server B,
* the Route pointer would point at its own object ('this').
*
* A
* / \
* + B C
* / \ \
* D E F
* / \
* * G H
*
* We only run this algorithm when a server is created, as
* the routes remain constant while ever the server exists, and
* do not need to be re-calculated.
*/
Route = Above;
if (Route == Utils->TreeRoot)
{
Route = this;
}
else
{
while (this->Route->GetParent() != Utils->TreeRoot)
{
this->Route = Route->GetParent();
}
}
/* Because recursive code is slow and takes a lot of resources,
* we store two representations of the server tree. The first
* is a recursive structure where each server references its
* children and its parent, which is used for netbursts and
* netsplits to dump the whole dataset to the other server,
* and the second is used for very fast lookups when routing
* messages and is instead a hash_map, where each item can
* be referenced by its server name. The AddHashEntry()
* call below automatically inserts each TreeServer class
* into the hash_map as it is created. There is a similar
* maintainance call in the destructor to tidy up deleted
* servers.
*/
this->AddHashEntry();
}
int TreeServer::QuitUsers(const std::string &reason)
{
const char* reason_s = reason.c_str();
std::vector<userrec*> time_to_die;
for (user_hash::iterator n = ServerInstance->clientlist->begin(); n != ServerInstance->clientlist->end(); n++)
{
if (!strcmp(n->second->server, this->ServerName.c_str()))
{
time_to_die.push_back(n->second);
}
}
for (std::vector<userrec*>::iterator n = time_to_die.begin(); n != time_to_die.end(); n++)
{
userrec* a = (userrec*)*n;
if (!IS_LOCAL(a))
{
if (ServerInstance->Config->HideSplits)
userrec::QuitUser(ServerInstance, a, "*.net *.split", reason_s);
else
userrec::QuitUser(ServerInstance, a, reason_s);
if (this->Utils->quiet_bursts)
ServerInstance->GlobalCulls.MakeSilent(a);
}
}
return time_to_die.size();
}
/** This method is used to add the structure to the
* hash_map for linear searches. It is only called
* by the constructors.
*/
void TreeServer::AddHashEntry()
{
server_hash::iterator iter = Utils->serverlist.find(this->ServerName.c_str());
if (iter == Utils->serverlist.end())
Utils->serverlist[this->ServerName.c_str()] = this;
}
/** This method removes the reference to this object
* from the hash_map which is used for linear searches.
* It is only called by the default destructor.
*/
void TreeServer::DelHashEntry()
{
server_hash::iterator iter = Utils->serverlist.find(this->ServerName.c_str());
if (iter != Utils->serverlist.end())
Utils->serverlist.erase(iter);
}
/** These accessors etc should be pretty self-
* explanitory.
*/
TreeServer* TreeServer::GetRoute()
{
return Route;
}
std::string TreeServer::GetName()
{
return ServerName.c_str();
}
std::string TreeServer::GetDesc()
{
return ServerDesc;
}
std::string TreeServer::GetVersion()
{
return VersionString;
}
void TreeServer::SetNextPingTime(time_t t)
{
this->NextPing = t;
LastPingWasGood = false;
}
time_t TreeServer::NextPingTime()
{
return NextPing;
}
bool TreeServer::AnsweredLastPing()
{
return LastPingWasGood;
}
void TreeServer::SetPingFlag()
{
LastPingWasGood = true;
}
int TreeServer::GetUserCount()
{
return UserCount;
}
void TreeServer::AddUserCount()
{
UserCount++;
}
void TreeServer::DelUserCount()
{
UserCount--;
}
int TreeServer::GetOperCount()
{
return OperCount;
}
TreeSocket* TreeServer::GetSocket()
{
return Socket;
}
TreeServer* TreeServer::GetParent()
{
return Parent;
}
void TreeServer::SetVersion(const std::string &Version)
{
VersionString = Version;
}
unsigned int TreeServer::ChildCount()
{
return Children.size();
}
TreeServer* TreeServer::GetChild(unsigned int n)
{
if (n < Children.size())
{
/* Make sure they cant request
* an out-of-range object. After
* all we know what these programmer
* types are like *grin*.
*/
return Children[n];
}
else
{
return NULL;
}
}
void TreeServer::AddChild(TreeServer* Child)
{
Children.push_back(Child);
}
bool TreeServer::DelChild(TreeServer* Child)
{
for (std::vector<TreeServer*>::iterator a = Children.begin(); a < Children.end(); a++)
{
if (*a == Child)
{
Children.erase(a);
return true;
}
}
return false;
}
/** Removes child nodes of this node, and of that node, etc etc.
* This is used during netsplits to automatically tidy up the
* server tree. It is slow, we don't use it for much else.
*/
bool TreeServer::Tidy()
{
bool stillchildren = true;
while (stillchildren)
{
stillchildren = false;
for (std::vector<TreeServer*>::iterator a = Children.begin(); a < Children.end(); a++)
{
TreeServer* s = (TreeServer*)*a;
s->Tidy();
Children.erase(a);
DELETE(s);
stillchildren = true;
break;
}
}
return true;
}
TreeServer::~TreeServer()
{
/* We'd better tidy up after ourselves, eh? */
this->DelHashEntry();
}
\ No newline at end of file +/* +------------------------------------+ + * | 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 "users.h" +#include "channels.h" +#include "modules.h" +#include "commands/cmd_whois.h" +#include "commands/cmd_stats.h" +#include "socket.h" +#include "wildcard.h" +#include "xline.h" +#include "transport.h" + +#include "m_spanningtree/utils.h" +#include "m_spanningtree/treeserver.h" + +/* $ModDep: m_spanningtree/utils.h m_spanningtree/treeserver.h */ + +TreeServer::TreeServer(SpanningTreeUtilities* Util, InspIRCd* Instance) : ServerInstance(Instance), Utils(Util) +{ + Parent = NULL; + ServerName.clear(); + ServerDesc.clear(); + VersionString.clear(); + UserCount = OperCount = 0; + rtt = LastPing = 0; + Hidden = false; + VersionString = ServerInstance->GetVersionString(); +} + +/** We use this constructor only to create the 'root' item, Utils->TreeRoot, which + * represents our own server. Therefore, it has no route, no parent, and + * no socket associated with it. Its version string is our own local version. + */ +TreeServer::TreeServer(SpanningTreeUtilities* Util, InspIRCd* Instance, std::string Name, std::string Desc) : ServerInstance(Instance), ServerName(Name.c_str()), ServerDesc(Desc), Utils(Util) +{ + Parent = NULL; + VersionString.clear(); + UserCount = ServerInstance->UserCount(); + OperCount = ServerInstance->OperCount(); + VersionString = ServerInstance->GetVersionString(); + Route = NULL; + Socket = NULL; /* Fix by brain */ + rtt = LastPing = 0; + Hidden = false; + AddHashEntry(); +} + +/** When we create a new server, we call this constructor to initialize it. + * This constructor initializes the server's Route and Parent, and sets up + * its ping counters so that it will be pinged one minute from now. + */ +TreeServer::TreeServer(SpanningTreeUtilities* Util, InspIRCd* Instance, std::string Name, std::string Desc, TreeServer* Above, TreeSocket* Sock, bool Hide) + : ServerInstance(Instance), Parent(Above), ServerName(Name.c_str()), ServerDesc(Desc), Socket(Sock), Utils(Util), Hidden(Hide) +{ + VersionString.clear(); + UserCount = OperCount = 0; + this->SetNextPingTime(time(NULL) + 60); + this->SetPingFlag(); + rtt = LastPing = 0; + /* find the 'route' for this server (e.g. the one directly connected + * to the local server, which we can use to reach it) + * + * In the following example, consider we have just added a TreeServer + * class for server G on our network, of which we are server A. + * To route traffic to G (marked with a *) we must send the data to + * B (marked with a +) so this algorithm initializes the 'Route' + * value to point at whichever server traffic must be routed through + * to get here. If we were to try this algorithm with server B, + * the Route pointer would point at its own object ('this'). + * + * A + * / \ + * + B C + * / \ \ + * D E F + * / \ + * * G H + * + * We only run this algorithm when a server is created, as + * the routes remain constant while ever the server exists, and + * do not need to be re-calculated. + */ + + Route = Above; + if (Route == Utils->TreeRoot) + { + Route = this; + } + else + { + while (this->Route->GetParent() != Utils->TreeRoot) + { + this->Route = Route->GetParent(); + } + } + + /* Because recursive code is slow and takes a lot of resources, + * we store two representations of the server tree. The first + * is a recursive structure where each server references its + * children and its parent, which is used for netbursts and + * netsplits to dump the whole dataset to the other server, + * and the second is used for very fast lookups when routing + * messages and is instead a hash_map, where each item can + * be referenced by its server name. The AddHashEntry() + * call below automatically inserts each TreeServer class + * into the hash_map as it is created. There is a similar + * maintainance call in the destructor to tidy up deleted + * servers. + */ + + this->AddHashEntry(); +} + +int TreeServer::QuitUsers(const std::string &reason) +{ + const char* reason_s = reason.c_str(); + std::vector<userrec*> time_to_die; + for (user_hash::iterator n = ServerInstance->clientlist->begin(); n != ServerInstance->clientlist->end(); n++) + { + if (!strcmp(n->second->server, this->ServerName.c_str())) + { + time_to_die.push_back(n->second); + } + } + for (std::vector<userrec*>::iterator n = time_to_die.begin(); n != time_to_die.end(); n++) + { + userrec* a = (userrec*)*n; + if (!IS_LOCAL(a)) + { + if (ServerInstance->Config->HideSplits) + userrec::QuitUser(ServerInstance, a, "*.net *.split", reason_s); + else + userrec::QuitUser(ServerInstance, a, reason_s); + + if (this->Utils->quiet_bursts) + ServerInstance->GlobalCulls.MakeSilent(a); + } + } + return time_to_die.size(); +} + +/** This method is used to add the structure to the + * hash_map for linear searches. It is only called + * by the constructors. + */ +void TreeServer::AddHashEntry() +{ + server_hash::iterator iter = Utils->serverlist.find(this->ServerName.c_str()); + if (iter == Utils->serverlist.end()) + Utils->serverlist[this->ServerName.c_str()] = this; +} + +/** This method removes the reference to this object + * from the hash_map which is used for linear searches. + * It is only called by the default destructor. + */ +void TreeServer::DelHashEntry() +{ + server_hash::iterator iter = Utils->serverlist.find(this->ServerName.c_str()); + if (iter != Utils->serverlist.end()) + Utils->serverlist.erase(iter); +} + +/** These accessors etc should be pretty self- + * explanitory. + */ +TreeServer* TreeServer::GetRoute() +{ + return Route; +} + +std::string TreeServer::GetName() +{ + return ServerName.c_str(); +} + +std::string TreeServer::GetDesc() +{ + return ServerDesc; +} + +std::string TreeServer::GetVersion() +{ + return VersionString; +} + +void TreeServer::SetNextPingTime(time_t t) +{ + this->NextPing = t; + LastPingWasGood = false; +} + +time_t TreeServer::NextPingTime() +{ + return NextPing; +} + +bool TreeServer::AnsweredLastPing() +{ + return LastPingWasGood; +} + +void TreeServer::SetPingFlag() +{ + LastPingWasGood = true; +} + +int TreeServer::GetUserCount() +{ + return UserCount; +} + +void TreeServer::AddUserCount() +{ + UserCount++; +} + +void TreeServer::DelUserCount() +{ + UserCount--; +} + +int TreeServer::GetOperCount() +{ + return OperCount; +} + +TreeSocket* TreeServer::GetSocket() +{ + return Socket; +} + +TreeServer* TreeServer::GetParent() +{ + return Parent; +} + +void TreeServer::SetVersion(const std::string &Version) +{ + VersionString = Version; +} + +unsigned int TreeServer::ChildCount() +{ + return Children.size(); +} + +TreeServer* TreeServer::GetChild(unsigned int n) +{ + if (n < Children.size()) + { + /* Make sure they cant request + * an out-of-range object. After + * all we know what these programmer + * types are like *grin*. + */ + return Children[n]; + } + else + { + return NULL; + } +} + +void TreeServer::AddChild(TreeServer* Child) +{ + Children.push_back(Child); +} + +bool TreeServer::DelChild(TreeServer* Child) +{ + for (std::vector<TreeServer*>::iterator a = Children.begin(); a < Children.end(); a++) + { + if (*a == Child) + { + Children.erase(a); + return true; + } + } + return false; +} + +/** Removes child nodes of this node, and of that node, etc etc. + * This is used during netsplits to automatically tidy up the + * server tree. It is slow, we don't use it for much else. + */ +bool TreeServer::Tidy() +{ + bool stillchildren = true; + while (stillchildren) + { + stillchildren = false; + for (std::vector<TreeServer*>::iterator a = Children.begin(); a < Children.end(); a++) + { + TreeServer* s = (TreeServer*)*a; + s->Tidy(); + Children.erase(a); + DELETE(s); + stillchildren = true; + break; + } + } + return true; +} + +TreeServer::~TreeServer() +{ + /* We'd better tidy up after ourselves, eh? */ + this->DelHashEntry(); +} + + diff --git a/src/modules/m_spanningtree/treeserver.h b/src/modules/m_spanningtree/treeserver.h index e942c1acc..514d6bc07 100644 --- a/src/modules/m_spanningtree/treeserver.h +++ b/src/modules/m_spanningtree/treeserver.h @@ -1 +1,186 @@ -/* +------------------------------------+
* | 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.
*
* ---------------------------------------------------
*/
#ifndef __TREESERVER_H__
#define __TREESERVER_H__
/** Each server in the tree is represented by one class of
* type TreeServer. A locally connected TreeServer can
* have a class of type TreeSocket associated with it, for
* remote servers, the TreeSocket entry will be NULL.
* Each server also maintains a pointer to its parent
* (NULL if this server is ours, at the top of the tree)
* and a pointer to its "Route" (see the comments in the
* constructors below), and also a dynamic list of pointers
* to its children which can be iterated recursively
* if required. Creating or deleting objects of type
i* TreeServer automatically maintains the hash_map of
* TreeServer items, deleting and inserting them as they
* are created and destroyed.
*/
class TreeServer : public classbase
{
InspIRCd* ServerInstance; /* Creator */
TreeServer* Parent; /* Parent entry */
TreeServer* Route; /* Route entry */
std::vector<TreeServer*> Children; /* List of child objects */
irc::string ServerName; /* Server's name */
std::string ServerDesc; /* Server's description */
std::string VersionString; /* Version string or empty string */
int UserCount; /* Not used in this version */
int OperCount; /* Not used in this version */
TreeSocket* Socket; /* For directly connected servers this points at the socket object */
time_t NextPing; /* After this time, the server should be PINGed*/
bool LastPingWasGood; /* True if the server responded to the last PING with a PONG */
SpanningTreeUtilities* Utils; /* Utility class */
public:
bool Warned; /* True if we've warned opers about high latency on this server */
/** We don't use this constructor. Its a dummy, and won't cause any insertion
* of the TreeServer into the hash_map. See below for the two we DO use.
*/
TreeServer(SpanningTreeUtilities* Util, InspIRCd* Instance);
/** We use this constructor only to create the 'root' item, Utils->TreeRoot, which
* represents our own server. Therefore, it has no route, no parent, and
* no socket associated with it. Its version string is our own local version.
*/
TreeServer(SpanningTreeUtilities* Util, InspIRCd* Instance, std::string Name, std::string Desc);
/** When we create a new server, we call this constructor to initialize it.
* This constructor initializes the server's Route and Parent, and sets up
* its ping counters so that it will be pinged one minute from now.
*/
TreeServer(SpanningTreeUtilities* Util, InspIRCd* Instance, std::string Name, std::string Desc, TreeServer* Above, TreeSocket* Sock, bool Hide);
int QuitUsers(const std::string &reason);
/** This method is used to add the structure to the
* hash_map for linear searches. It is only called
* by the constructors.
*/
void AddHashEntry();
/** This method removes the reference to this object
* from the hash_map which is used for linear searches.
* It is only called by the default destructor.
*/
void DelHashEntry();
/** Get route.
* The 'route' is defined as the locally-
* connected server which can be used to reach this server.
*/
TreeServer* GetRoute();
/** Get server name
*/
std::string GetName();
/** Get server description (GECOS)
*/
std::string GetDesc();
/** Get server version string
*/
std::string GetVersion();
/** Set time we are next due to ping this server
*/
void SetNextPingTime(time_t t);
/** Get the time we are next due to ping this server
*/
time_t NextPingTime();
/** Time of last ping used to calculate this->rtt below
*/
time_t LastPing;
/** Round trip time of last ping
*/
time_t rtt;
/** True if this server is hidden
*/
bool Hidden;
/** True if the server answered their last ping
*/
bool AnsweredLastPing();
/** Set the server as responding to its last ping
*/
void SetPingFlag();
/** Get the number of users on this server for MAP
*/
int GetUserCount();
/** Increment the user counter
*/
void AddUserCount();
/** Decrement the user counter
*/
void DelUserCount();
/** Get the oper count for this server
*/
int GetOperCount();
/** Get the TreeSocket pointer for local servers.
* For remote servers, this returns NULL.
*/
TreeSocket* GetSocket();
/** Get the parent server.
* For the root node, this returns NULL.
*/
TreeServer* GetParent();
/** Set the server version string
*/
void SetVersion(const std::string &Version);
/** Return number of child servers
*/
unsigned int ChildCount();
/** Return a child server indexed 0..n
*/
TreeServer* GetChild(unsigned int n);
/** Add a child server
*/
void AddChild(TreeServer* Child);
/** Delete a child server, return false if it didn't exist.
*/
bool DelChild(TreeServer* Child);
/** Removes child nodes of this node, and of that node, etc etc.
* This is used during netsplits to automatically tidy up the
* server tree. It is slow, we don't use it for much else.
*/
bool Tidy();
/** Destructor
*/
~TreeServer();
};
#endif
\ No newline at end of file +/* +------------------------------------+ + * | 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. + * + * --------------------------------------------------- + */ + +#ifndef __TREESERVER_H__ +#define __TREESERVER_H__ + +/** Each server in the tree is represented by one class of + * type TreeServer. A locally connected TreeServer can + * have a class of type TreeSocket associated with it, for + * remote servers, the TreeSocket entry will be NULL. + * Each server also maintains a pointer to its parent + * (NULL if this server is ours, at the top of the tree) + * and a pointer to its "Route" (see the comments in the + * constructors below), and also a dynamic list of pointers + * to its children which can be iterated recursively + * if required. Creating or deleting objects of type + i* TreeServer automatically maintains the hash_map of + * TreeServer items, deleting and inserting them as they + * are created and destroyed. + */ +class TreeServer : public classbase +{ + InspIRCd* ServerInstance; /* Creator */ + TreeServer* Parent; /* Parent entry */ + TreeServer* Route; /* Route entry */ + std::vector<TreeServer*> Children; /* List of child objects */ + irc::string ServerName; /* Server's name */ + std::string ServerDesc; /* Server's description */ + std::string VersionString; /* Version string or empty string */ + int UserCount; /* Not used in this version */ + int OperCount; /* Not used in this version */ + TreeSocket* Socket; /* For directly connected servers this points at the socket object */ + time_t NextPing; /* After this time, the server should be PINGed*/ + bool LastPingWasGood; /* True if the server responded to the last PING with a PONG */ + SpanningTreeUtilities* Utils; /* Utility class */ + + public: + + bool Warned; /* True if we've warned opers about high latency on this server */ + + /** We don't use this constructor. Its a dummy, and won't cause any insertion + * of the TreeServer into the hash_map. See below for the two we DO use. + */ + TreeServer(SpanningTreeUtilities* Util, InspIRCd* Instance); + + /** We use this constructor only to create the 'root' item, Utils->TreeRoot, which + * represents our own server. Therefore, it has no route, no parent, and + * no socket associated with it. Its version string is our own local version. + */ + TreeServer(SpanningTreeUtilities* Util, InspIRCd* Instance, std::string Name, std::string Desc); + + /** When we create a new server, we call this constructor to initialize it. + * This constructor initializes the server's Route and Parent, and sets up + * its ping counters so that it will be pinged one minute from now. + */ + TreeServer(SpanningTreeUtilities* Util, InspIRCd* Instance, std::string Name, std::string Desc, TreeServer* Above, TreeSocket* Sock, bool Hide); + + int QuitUsers(const std::string &reason); + + /** This method is used to add the structure to the + * hash_map for linear searches. It is only called + * by the constructors. + */ + void AddHashEntry(); + + /** This method removes the reference to this object + * from the hash_map which is used for linear searches. + * It is only called by the default destructor. + */ + void DelHashEntry(); + + /** Get route. + * The 'route' is defined as the locally- + * connected server which can be used to reach this server. + */ + TreeServer* GetRoute(); + + /** Get server name + */ + std::string GetName(); + + /** Get server description (GECOS) + */ + std::string GetDesc(); + + /** Get server version string + */ + std::string GetVersion(); + + /** Set time we are next due to ping this server + */ + void SetNextPingTime(time_t t); + + /** Get the time we are next due to ping this server + */ + time_t NextPingTime(); + + /** Time of last ping used to calculate this->rtt below + */ + time_t LastPing; + + /** Round trip time of last ping + */ + time_t rtt; + + /** True if this server is hidden + */ + bool Hidden; + + /** True if the server answered their last ping + */ + bool AnsweredLastPing(); + + /** Set the server as responding to its last ping + */ + void SetPingFlag(); + + /** Get the number of users on this server for MAP + */ + int GetUserCount(); + + /** Increment the user counter + */ + void AddUserCount(); + + /** Decrement the user counter + */ + void DelUserCount(); + + /** Get the oper count for this server + */ + int GetOperCount(); + + /** Get the TreeSocket pointer for local servers. + * For remote servers, this returns NULL. + */ + TreeSocket* GetSocket(); + + /** Get the parent server. + * For the root node, this returns NULL. + */ + TreeServer* GetParent(); + + /** Set the server version string + */ + void SetVersion(const std::string &Version); + + /** Return number of child servers + */ + unsigned int ChildCount(); + + /** Return a child server indexed 0..n + */ + TreeServer* GetChild(unsigned int n); + + /** Add a child server + */ + void AddChild(TreeServer* Child); + + /** Delete a child server, return false if it didn't exist. + */ + bool DelChild(TreeServer* Child); + + /** Removes child nodes of this node, and of that node, etc etc. + * This is used during netsplits to automatically tidy up the + * server tree. It is slow, we don't use it for much else. + */ + bool Tidy(); + + /** Destructor + */ + ~TreeServer(); + +}; + +#endif diff --git a/src/modules/m_spanningtree/treesocket.h b/src/modules/m_spanningtree/treesocket.h index bd99c1480..fae22638d 100644 --- a/src/modules/m_spanningtree/treesocket.h +++ b/src/modules/m_spanningtree/treesocket.h @@ -1 +1,413 @@ -/* +------------------------------------+
* | 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.
*
* ---------------------------------------------------
*/
#ifndef __TREESOCKET_H__
#define __TREESOCKET_H__
#include "configreader.h"
#include "users.h"
#include "channels.h"
#include "modules.h"
#include "commands/cmd_whois.h"
#include "commands/cmd_stats.h"
#include "socket.h"
#include "inspircd.h"
#include "wildcard.h"
#include "xline.h"
#include "transport.h"
#include "m_spanningtree/utils.h"
/*
* The server list in InspIRCd is maintained as two structures
* which hold the data in different ways. Most of the time, we
* want to very quicky obtain three pieces of information:
*
* (1) The information on a server
* (2) The information on the server we must send data through
* to actually REACH the server we're after
* (3) Potentially, the child/parent objects of this server
*
* The InspIRCd spanning protocol provides easy access to these
* by storing the data firstly in a recursive structure, where
* each item references its parent item, and a dynamic list
* of child items, and another structure which stores the items
* hashed, linearly. This means that if we want to find a server
* by name quickly, we can look it up in the hash, avoiding
* any O(n) lookups. If however, during a split or sync, we want
* to apply an operation to a server, and any of its child objects
* we can resort to recursion to walk the tree structure.
* Any socket can have one of five states at any one time.
* The LISTENER state indicates a socket which is listening
* for connections. It cannot receive data itself, only incoming
* sockets.
* The CONNECTING state indicates an outbound socket which is
* waiting to be writeable.
* The WAIT_AUTH_1 state indicates the socket is outbound and
* has successfully connected, but has not yet sent and received
* SERVER strings.
* The WAIT_AUTH_2 state indicates that the socket is inbound
* (allocated by a LISTENER) but has not yet sent and received
* SERVER strings.
* The CONNECTED state represents a fully authorized, fully
* connected server.
*/
enum ServerState { LISTENER, CONNECTING, WAIT_AUTH_1, WAIT_AUTH_2, CONNECTED };
/** Every SERVER connection inbound or outbound is represented by
* an object of type TreeSocket.
* TreeSockets, being inherited from InspSocket, can be tied into
* the core socket engine, and we cn therefore receive activity events
* for them, just like activex objects on speed. (yes really, that
* is a technical term!) Each of these which relates to a locally
* connected server is assocated with it, by hooking it onto a
* TreeSocket class using its constructor. In this way, we can
* maintain a list of servers, some of which are directly connected,
* some of which are not.
*/
class TreeSocket : public InspSocket
{
SpanningTreeUtilities* Utils; /* Utility class */
std::string myhost; /* Canonical hostname */
std::string in_buffer; /* Input buffer */
ServerState LinkState; /* Link state */
std::string InboundServerName; /* Server name sent to us by other side */
std::string InboundDescription; /* Server description (GECOS) sent to us by the other side */
int num_lost_users; /* Users lost in split */
int num_lost_servers; /* Servers lost in split */
time_t NextPing; /* Time when we are due to ping this server */
bool LastPingWasGood; /* Responded to last ping we sent? */
bool bursting; /* True if not finished bursting yet */
unsigned int keylength; /* Is this still used? */
std::string ModuleList; /* Module list of other server from CAPAB */
std::map<std::string,std::string> CapKeys; /* CAPAB keys from other server */
Module* Hook; /* I/O hooking module that we're attached to for this socket */
std::string ourchallenge; /* Challenge sent for challenge/response */
std::string theirchallenge; /* Challenge recv for challenge/response */
std::string OutboundPass; /* Outbound password */
bool sentcapab; /* Have sent CAPAB already */
public:
/** Because most of the I/O gubbins are encapsulated within
* InspSocket, we just call the superclass constructor for
* most of the action, and append a few of our own values
* to it.
*/
TreeSocket(SpanningTreeUtilities* Util, InspIRCd* SI, std::string host, int port, bool listening, unsigned long maxtime, Module* HookMod = NULL);
/** Because most of the I/O gubbins are encapsulated within
* InspSocket, we just call the superclass constructor for
* most of the action, and append a few of our own values
* to it.
*/
TreeSocket(SpanningTreeUtilities* Util, InspIRCd* SI, std::string host, int port, bool listening, unsigned long maxtime, const std::string &ServerName, const std::string &bindto, Module* HookMod = NULL);
/** When a listening socket gives us a new file descriptor,
* we must associate it with a socket without creating a new
* connection. This constructor is used for this purpose.
*/
TreeSocket(SpanningTreeUtilities* Util, InspIRCd* SI, int newfd, char* ip, Module* HookMod = NULL);
/** Get link state
*/
ServerState GetLinkState();
/** Get challenge set in our CAPAB for challenge/response
*/
const std::string& GetOurChallenge();
/** Get challenge set in our CAPAB for challenge/response
*/
void SetOurChallenge(const std::string &c);
/** Get challenge set in their CAPAB for challenge/response
*/
const std::string& GetTheirChallenge();
/** Get challenge set in their CAPAB for challenge/response
*/
void SetTheirChallenge(const std::string &c);
/** Compare two passwords based on authentication scheme
*/
bool ComparePass(const std::string &ours, const std::string &theirs);
/** Return the module which we are hooking to for I/O encapsulation
*/
Module* GetHook();
/** Destructor
*/
~TreeSocket();
/** Generate random string used for challenge-response auth
*/
std::string RandString(unsigned int length);
/** Construct a password, optionally hashed with the other side's
* challenge string
*/
std::string MakePass(const std::string &password, const std::string &challenge);
/** When an outbound connection finishes connecting, we receive
* this event, and must send our SERVER string to the other
* side. If the other side is happy, as outlined in the server
* to server docs on the inspircd.org site, the other side
* will then send back its own server string.
*/
virtual bool OnConnected();
/** Handle socket error event
*/
virtual void OnError(InspSocketError e);
/** Sends an error to the remote server, and displays it locally to show
* that it was sent.
*/
void SendError(const std::string &errormessage);
/** Handle socket disconnect event
*/
virtual int OnDisconnect();
/** Recursively send the server tree with distances as hops.
* This is used during network burst to inform the other server
* (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 hopcounts given
* by this function are relative, this doesn't matter so long as
* they are all >1, as all the remote servers re-calculate them
* to be relative too, with themselves as hop 0.
*/
void SendServers(TreeServer* Current, TreeServer* s, int hops);
/** Returns my capabilities as a string
*/
std::string MyCapabilities();
/** Send my capabilities to the remote side
*/
void SendCapabilities();
/* Check a comma seperated list for an item */
bool HasItem(const std::string &list, const std::string &item);
/* Isolate and return the elements that are different between two comma seperated lists */
std::string ListDifference(const std::string &one, const std::string &two);
bool Capab(const std::deque<std::string> ¶ms);
/** This function forces this server to quit, removing this server
* and any users on it (and servers and users below that, etc etc).
* It's very slow and pretty clunky, but luckily unless your network
* is having a REAL bad hair day, this function shouldnt be called
* too many times a month ;-)
*/
void SquitServer(std::string &from, TreeServer* Current);
/** This is a wrapper function for SquitServer above, which
* does some validation first and passes on the SQUIT to all
* other remaining servers.
*/
void Squit(TreeServer* Current, const std::string &reason);
/** FMODE command - server mode with timestamp checks */
bool ForceMode(const std::string &source, std::deque<std::string> ¶ms);
/** FTOPIC command */
bool ForceTopic(const std::string &source, std::deque<std::string> ¶ms);
/** FJOIN, similar to TS6 SJOIN, but not quite. */
bool ForceJoin(const std::string &source, std::deque<std::string> ¶ms);
/** NICK command */
bool IntroduceClient(const std::string &source, std::deque<std::string> ¶ms);
/** 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.
*/
void SendFJoins(TreeServer* Current, chanrec* c);
/** Send G, Q, Z and E lines */
void SendXLines(TreeServer* Current);
/** Send channel modes and topics */
void SendChannelModes(TreeServer* Current);
/** send all users and their oper state/modes */
void SendUsers(TreeServer* Current);
/** 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
* users require their servers to exist, and channels require their
* users to exist. You get the idea.
*/
void DoBurst(TreeServer* s);
/** This function is called when we receive data from a remote
* server. We buffer the data in a std::string (it doesnt stay
* there for long), reading using InspSocket::Read() which can
* read up to 16 kilobytes in one operation.
*
* IF THIS FUNCTION RETURNS FALSE, THE CORE CLOSES AND DELETES
* THE SOCKET OBJECT FOR US.
*/
virtual bool OnDataReady();
/** Send one or more complete lines down the socket
*/
int WriteLine(std::string line);
/** Handle ERROR command */
bool Error(std::deque<std::string> ¶ms);
/** remote MOTD. leet, huh? */
bool Motd(const std::string &prefix, std::deque<std::string> ¶ms);
/** remote ADMIN. leet, huh? */
bool Admin(const std::string &prefix, std::deque<std::string> ¶ms);
/** Remote MODULES */
bool Modules(const std::string &prefix, std::deque<std::string> ¶ms);
bool Stats(const std::string &prefix, std::deque<std::string> ¶ms);
/** Because the core won't let users or even SERVERS set +o,
* we use the OPERTYPE command to do this.
*/
bool OperType(const std::string &prefix, std::deque<std::string> ¶ms);
/** Because Andy insists that services-compatible servers must
* implement SVSNICK and SVSJOIN, that's exactly what we do :p
*/
bool ForceNick(const std::string &prefix, std::deque<std::string> ¶ms);
bool OperQuit(const std::string &prefix, std::deque<std::string> ¶ms);
/** SVSJOIN
*/
bool ServiceJoin(const std::string &prefix, std::deque<std::string> ¶ms);
/** REHASH
*/
bool RemoteRehash(const std::string &prefix, std::deque<std::string> ¶ms);
/** KILL
*/
bool RemoteKill(const std::string &prefix, std::deque<std::string> ¶ms);
/** PONG
*/
bool LocalPong(const std::string &prefix, std::deque<std::string> ¶ms);
/** METADATA
*/
bool MetaData(const std::string &prefix, std::deque<std::string> ¶ms);
/** VERSION
*/
bool ServerVersion(const std::string &prefix, std::deque<std::string> ¶ms);
/** CHGHOST
*/
bool ChangeHost(const std::string &prefix, std::deque<std::string> ¶ms);
/** ADDLINE
*/
bool AddLine(const std::string &prefix, std::deque<std::string> ¶ms);
/** CHGNAME
*/
bool ChangeName(const std::string &prefix, std::deque<std::string> ¶ms);
/** WHOIS
*/
bool Whois(const std::string &prefix, std::deque<std::string> ¶ms);
/** PUSH
*/
bool Push(const std::string &prefix, std::deque<std::string> ¶ms);
/** SETTIME
*/
bool HandleSetTime(const std::string &prefix, std::deque<std::string> ¶ms);
/** TIME
*/
bool Time(const std::string &prefix, std::deque<std::string> ¶ms);
/** PING
*/
bool LocalPing(const std::string &prefix, std::deque<std::string> ¶ms);
/** Remove all modes from a channel, including statusmodes (+qaovh etc), simplemodes, parameter modes.
* This does not update the timestamp of the target channel, this must be done seperately.
*/
bool RemoveStatus(const std::string &prefix, std::deque<std::string> ¶ms);
/** <- (remote) <- SERVER
*/
bool RemoteServer(const std::string &prefix, std::deque<std::string> ¶ms);
/** (local) -> SERVER
*/
bool Outbound_Reply_Server(std::deque<std::string> ¶ms);
/** (local) <- SERVER
*/
bool Inbound_Server(std::deque<std::string> ¶ms);
/** Handle netsplit
*/
void Split(const std::string &line, std::deque<std::string> &n);
/** Process complete line from buffer
*/
bool ProcessLine(std::string &line);
/** Get this server's name
*/
virtual std::string GetName();
/** Handle socket timeout from connect()
*/
virtual void OnTimeout();
/** Handle socket close event
*/
virtual void OnClose();
/** Handle incoming connection event
*/
virtual int OnIncomingConnection(int newsock, char* ip);
};
/* Used to validate the value lengths of multiple parameters for a command */
struct cmd_validation
{
const char* item;
size_t param;
size_t length;
};
/* Used to validate the length values in CAPAB CAPABILITIES */
struct cap_validation
{
const char* reason;
const char* key;
size_t size;
};
#endif
\ No newline at end of file +/* +------------------------------------+ + * | 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. + * + * --------------------------------------------------- + */ + +#ifndef __TREESOCKET_H__ +#define __TREESOCKET_H__ + +#include "configreader.h" +#include "users.h" +#include "channels.h" +#include "modules.h" +#include "commands/cmd_whois.h" +#include "commands/cmd_stats.h" +#include "socket.h" +#include "inspircd.h" +#include "wildcard.h" +#include "xline.h" +#include "transport.h" + +#include "m_spanningtree/utils.h" + +/* + * The server list in InspIRCd is maintained as two structures + * which hold the data in different ways. Most of the time, we + * want to very quicky obtain three pieces of information: + * + * (1) The information on a server + * (2) The information on the server we must send data through + * to actually REACH the server we're after + * (3) Potentially, the child/parent objects of this server + * + * The InspIRCd spanning protocol provides easy access to these + * by storing the data firstly in a recursive structure, where + * each item references its parent item, and a dynamic list + * of child items, and another structure which stores the items + * hashed, linearly. This means that if we want to find a server + * by name quickly, we can look it up in the hash, avoiding + * any O(n) lookups. If however, during a split or sync, we want + * to apply an operation to a server, and any of its child objects + * we can resort to recursion to walk the tree structure. + * Any socket can have one of five states at any one time. + * The LISTENER state indicates a socket which is listening + * for connections. It cannot receive data itself, only incoming + * sockets. + * The CONNECTING state indicates an outbound socket which is + * waiting to be writeable. + * The WAIT_AUTH_1 state indicates the socket is outbound and + * has successfully connected, but has not yet sent and received + * SERVER strings. + * The WAIT_AUTH_2 state indicates that the socket is inbound + * (allocated by a LISTENER) but has not yet sent and received + * SERVER strings. + * The CONNECTED state represents a fully authorized, fully + * connected server. + */ +enum ServerState { LISTENER, CONNECTING, WAIT_AUTH_1, WAIT_AUTH_2, CONNECTED }; + +/** Every SERVER connection inbound or outbound is represented by + * an object of type TreeSocket. + * TreeSockets, being inherited from InspSocket, can be tied into + * the core socket engine, and we cn therefore receive activity events + * for them, just like activex objects on speed. (yes really, that + * is a technical term!) Each of these which relates to a locally + * connected server is assocated with it, by hooking it onto a + * TreeSocket class using its constructor. In this way, we can + * maintain a list of servers, some of which are directly connected, + * some of which are not. + */ +class TreeSocket : public InspSocket +{ + SpanningTreeUtilities* Utils; /* Utility class */ + std::string myhost; /* Canonical hostname */ + std::string in_buffer; /* Input buffer */ + ServerState LinkState; /* Link state */ + std::string InboundServerName; /* Server name sent to us by other side */ + std::string InboundDescription; /* Server description (GECOS) sent to us by the other side */ + int num_lost_users; /* Users lost in split */ + int num_lost_servers; /* Servers lost in split */ + time_t NextPing; /* Time when we are due to ping this server */ + bool LastPingWasGood; /* Responded to last ping we sent? */ + bool bursting; /* True if not finished bursting yet */ + unsigned int keylength; /* Is this still used? */ + std::string ModuleList; /* Module list of other server from CAPAB */ + std::map<std::string,std::string> CapKeys; /* CAPAB keys from other server */ + Module* Hook; /* I/O hooking module that we're attached to for this socket */ + std::string ourchallenge; /* Challenge sent for challenge/response */ + std::string theirchallenge; /* Challenge recv for challenge/response */ + std::string OutboundPass; /* Outbound password */ + bool sentcapab; /* Have sent CAPAB already */ + public: + + /** Because most of the I/O gubbins are encapsulated within + * InspSocket, we just call the superclass constructor for + * most of the action, and append a few of our own values + * to it. + */ + TreeSocket(SpanningTreeUtilities* Util, InspIRCd* SI, std::string host, int port, bool listening, unsigned long maxtime, Module* HookMod = NULL); + + /** Because most of the I/O gubbins are encapsulated within + * InspSocket, we just call the superclass constructor for + * most of the action, and append a few of our own values + * to it. + */ + TreeSocket(SpanningTreeUtilities* Util, InspIRCd* SI, std::string host, int port, bool listening, unsigned long maxtime, const std::string &ServerName, const std::string &bindto, Module* HookMod = NULL); + + /** When a listening socket gives us a new file descriptor, + * we must associate it with a socket without creating a new + * connection. This constructor is used for this purpose. + */ + TreeSocket(SpanningTreeUtilities* Util, InspIRCd* SI, int newfd, char* ip, Module* HookMod = NULL); + + /** Get link state + */ + ServerState GetLinkState(); + + /** Get challenge set in our CAPAB for challenge/response + */ + const std::string& GetOurChallenge(); + + /** Get challenge set in our CAPAB for challenge/response + */ + void SetOurChallenge(const std::string &c); + + /** Get challenge set in their CAPAB for challenge/response + */ + const std::string& GetTheirChallenge(); + + /** Get challenge set in their CAPAB for challenge/response + */ + void SetTheirChallenge(const std::string &c); + + /** Compare two passwords based on authentication scheme + */ + bool ComparePass(const std::string &ours, const std::string &theirs); + + /** Return the module which we are hooking to for I/O encapsulation + */ + Module* GetHook(); + + /** Destructor + */ + ~TreeSocket(); + + /** Generate random string used for challenge-response auth + */ + std::string RandString(unsigned int length); + + /** Construct a password, optionally hashed with the other side's + * challenge string + */ + std::string MakePass(const std::string &password, const std::string &challenge); + + /** When an outbound connection finishes connecting, we receive + * this event, and must send our SERVER string to the other + * side. If the other side is happy, as outlined in the server + * to server docs on the inspircd.org site, the other side + * will then send back its own server string. + */ + virtual bool OnConnected(); + + /** Handle socket error event + */ + virtual void OnError(InspSocketError e); + + /** Sends an error to the remote server, and displays it locally to show + * that it was sent. + */ + void SendError(const std::string &errormessage); + + /** Handle socket disconnect event + */ + virtual int OnDisconnect(); + + /** Recursively send the server tree with distances as hops. + * This is used during network burst to inform the other server + * (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 hopcounts given + * by this function are relative, this doesn't matter so long as + * they are all >1, as all the remote servers re-calculate them + * to be relative too, with themselves as hop 0. + */ + void SendServers(TreeServer* Current, TreeServer* s, int hops); + + /** Returns my capabilities as a string + */ + std::string MyCapabilities(); + + /** Send my capabilities to the remote side + */ + void SendCapabilities(); + + /* Check a comma seperated list for an item */ + bool HasItem(const std::string &list, const std::string &item); + + /* Isolate and return the elements that are different between two comma seperated lists */ + std::string ListDifference(const std::string &one, const std::string &two); + + bool Capab(const std::deque<std::string> ¶ms); + + /** This function forces this server to quit, removing this server + * and any users on it (and servers and users below that, etc etc). + * It's very slow and pretty clunky, but luckily unless your network + * is having a REAL bad hair day, this function shouldnt be called + * too many times a month ;-) + */ + void SquitServer(std::string &from, TreeServer* Current); + + /** This is a wrapper function for SquitServer above, which + * does some validation first and passes on the SQUIT to all + * other remaining servers. + */ + void Squit(TreeServer* Current, const std::string &reason); + + /** FMODE command - server mode with timestamp checks */ + bool ForceMode(const std::string &source, std::deque<std::string> ¶ms); + + /** FTOPIC command */ + bool ForceTopic(const std::string &source, std::deque<std::string> ¶ms); + + /** FJOIN, similar to TS6 SJOIN, but not quite. */ + bool ForceJoin(const std::string &source, std::deque<std::string> ¶ms); + + /** NICK command */ + bool IntroduceClient(const std::string &source, std::deque<std::string> ¶ms); + + /** 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. + */ + void SendFJoins(TreeServer* Current, chanrec* c); + + /** Send G, Q, Z and E lines */ + void SendXLines(TreeServer* Current); + + /** Send channel modes and topics */ + void SendChannelModes(TreeServer* Current); + + /** send all users and their oper state/modes */ + void SendUsers(TreeServer* Current); + + /** 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 + * users require their servers to exist, and channels require their + * users to exist. You get the idea. + */ + void DoBurst(TreeServer* s); + + /** This function is called when we receive data from a remote + * server. We buffer the data in a std::string (it doesnt stay + * there for long), reading using InspSocket::Read() which can + * read up to 16 kilobytes in one operation. + * + * IF THIS FUNCTION RETURNS FALSE, THE CORE CLOSES AND DELETES + * THE SOCKET OBJECT FOR US. + */ + virtual bool OnDataReady(); + + /** Send one or more complete lines down the socket + */ + int WriteLine(std::string line); + + /** Handle ERROR command */ + bool Error(std::deque<std::string> ¶ms); + + /** remote MOTD. leet, huh? */ + bool Motd(const std::string &prefix, std::deque<std::string> ¶ms); + + /** remote ADMIN. leet, huh? */ + bool Admin(const std::string &prefix, std::deque<std::string> ¶ms); + + /** Remote MODULES */ + bool Modules(const std::string &prefix, std::deque<std::string> ¶ms); + + bool Stats(const std::string &prefix, std::deque<std::string> ¶ms); + + /** Because the core won't let users or even SERVERS set +o, + * we use the OPERTYPE command to do this. + */ + bool OperType(const std::string &prefix, std::deque<std::string> ¶ms); + + /** Because Andy insists that services-compatible servers must + * implement SVSNICK and SVSJOIN, that's exactly what we do :p + */ + bool ForceNick(const std::string &prefix, std::deque<std::string> ¶ms); + + bool OperQuit(const std::string &prefix, std::deque<std::string> ¶ms); + + /** SVSJOIN + */ + bool ServiceJoin(const std::string &prefix, std::deque<std::string> ¶ms); + + /** REHASH + */ + bool RemoteRehash(const std::string &prefix, std::deque<std::string> ¶ms); + + /** KILL + */ + bool RemoteKill(const std::string &prefix, std::deque<std::string> ¶ms); + + /** PONG + */ + bool LocalPong(const std::string &prefix, std::deque<std::string> ¶ms); + + /** METADATA + */ + bool MetaData(const std::string &prefix, std::deque<std::string> ¶ms); + + /** VERSION + */ + bool ServerVersion(const std::string &prefix, std::deque<std::string> ¶ms); + + /** CHGHOST + */ + bool ChangeHost(const std::string &prefix, std::deque<std::string> ¶ms); + + /** ADDLINE + */ + bool AddLine(const std::string &prefix, std::deque<std::string> ¶ms); + + /** CHGNAME + */ + bool ChangeName(const std::string &prefix, std::deque<std::string> ¶ms); + + /** WHOIS + */ + bool Whois(const std::string &prefix, std::deque<std::string> ¶ms); + + /** PUSH + */ + bool Push(const std::string &prefix, std::deque<std::string> ¶ms); + + /** SETTIME + */ + bool HandleSetTime(const std::string &prefix, std::deque<std::string> ¶ms); + + /** TIME + */ + bool Time(const std::string &prefix, std::deque<std::string> ¶ms); + + /** PING + */ + bool LocalPing(const std::string &prefix, std::deque<std::string> ¶ms); + + /** Remove all modes from a channel, including statusmodes (+qaovh etc), simplemodes, parameter modes. + * This does not update the timestamp of the target channel, this must be done seperately. + */ + bool RemoveStatus(const std::string &prefix, std::deque<std::string> ¶ms); + + /** <- (remote) <- SERVER + */ + bool RemoteServer(const std::string &prefix, std::deque<std::string> ¶ms); + + /** (local) -> SERVER + */ + bool Outbound_Reply_Server(std::deque<std::string> ¶ms); + + /** (local) <- SERVER + */ + bool Inbound_Server(std::deque<std::string> ¶ms); + + /** Handle netsplit + */ + void Split(const std::string &line, std::deque<std::string> &n); + + /** Process complete line from buffer + */ + bool ProcessLine(std::string &line); + + /** Get this server's name + */ + virtual std::string GetName(); + + /** Handle socket timeout from connect() + */ + virtual void OnTimeout(); + + /** Handle socket close event + */ + virtual void OnClose(); + + /** Handle incoming connection event + */ + virtual int OnIncomingConnection(int newsock, char* ip); +}; + +/* Used to validate the value lengths of multiple parameters for a command */ +struct cmd_validation +{ + const char* item; + size_t param; + size_t length; +}; + +/* Used to validate the length values in CAPAB CAPABILITIES */ +struct cap_validation +{ + const char* reason; + const char* key; + size_t size; +}; + +#endif + diff --git a/src/modules/m_spanningtree/treesocket1.cpp b/src/modules/m_spanningtree/treesocket1.cpp index ad2588cab..a907bb440 100644 --- a/src/modules/m_spanningtree/treesocket1.cpp +++ b/src/modules/m_spanningtree/treesocket1.cpp @@ -1 +1,1273 @@ -/* +------------------------------------+
* | 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 "users.h"
#include "channels.h"
#include "modules.h"
#include "commands/cmd_whois.h"
#include "commands/cmd_stats.h"
#include "socket.h"
#include "wildcard.h"
#include "xline.h"
#include "transport.h"
#include "m_hash.h"
#include "socketengine.h"
#include "m_spanningtree/main.h"
#include "m_spanningtree/utils.h"
#include "m_spanningtree/treeserver.h"
#include "m_spanningtree/link.h"
#include "m_spanningtree/treesocket.h"
#include "m_spanningtree/resolvers.h"
#include "m_spanningtree/handshaketimer.h"
/* $ModDep: m_spanningtree/timesynctimer.h m_spanningtree/resolvers.h m_spanningtree/main.h m_spanningtree/utils.h m_spanningtree/treeserver.h m_spanningtree/link.h m_spanningtree/treesocket.h m_hash.h */
/** Because most of the I/O gubbins are encapsulated within
* InspSocket, we just call the superclass constructor for
* most of the action, and append a few of our own values
* to it.
*/
TreeSocket::TreeSocket(SpanningTreeUtilities* Util, InspIRCd* SI, std::string host, int port, bool listening, unsigned long maxtime, Module* HookMod)
: InspSocket(SI, host, port, listening, maxtime), Utils(Util), Hook(HookMod)
{
myhost = host;
this->LinkState = LISTENER;
theirchallenge.clear();
ourchallenge.clear();
if (listening && Hook)
InspSocketHookRequest(this, (Module*)Utils->Creator, Hook).Send();
}
TreeSocket::TreeSocket(SpanningTreeUtilities* Util, InspIRCd* SI, std::string host, int port, bool listening, unsigned long maxtime, const std::string &ServerName, const std::string &bindto, Module* HookMod)
: InspSocket(SI, host, port, listening, maxtime, bindto), Utils(Util), Hook(HookMod)
{
myhost = ServerName;
theirchallenge.clear();
ourchallenge.clear();
this->LinkState = CONNECTING;
if (Hook)
InspSocketHookRequest(this, (Module*)Utils->Creator, Hook).Send();
}
/** When a listening socket gives us a new file descriptor,
* we must associate it with a socket without creating a new
* connection. This constructor is used for this purpose.
*/
TreeSocket::TreeSocket(SpanningTreeUtilities* Util, InspIRCd* SI, int newfd, char* ip, Module* HookMod)
: InspSocket(SI, newfd, ip), Utils(Util), Hook(HookMod)
{
this->LinkState = WAIT_AUTH_1;
theirchallenge.clear();
ourchallenge.clear();
sentcapab = false;
/* If we have a transport module hooked to the parent, hook the same module to this
* socket, and set a timer waiting for handshake before we send CAPAB etc.
*/
if (Hook)
InspSocketHookRequest(this, (Module*)Utils->Creator, Hook).Send();
Instance->Timers->AddTimer(new HandshakeTimer(Instance, this, &(Utils->LinkBlocks[0]), this->Utils, 1));
}
ServerState TreeSocket::GetLinkState()
{
return this->LinkState;
}
Module* TreeSocket::GetHook()
{
return this->Hook;
}
TreeSocket::~TreeSocket()
{
if (Hook)
InspSocketUnhookRequest(this, (Module*)Utils->Creator, Hook).Send();
Utils->DelBurstingServer(this);
}
const std::string& TreeSocket::GetOurChallenge()
{
return this->ourchallenge;
}
void TreeSocket::SetOurChallenge(const std::string &c)
{
this->ourchallenge = c;
}
const std::string& TreeSocket::GetTheirChallenge()
{
return this->theirchallenge;
}
void TreeSocket::SetTheirChallenge(const std::string &c)
{
this->theirchallenge = c;
}
std::string TreeSocket::MakePass(const std::string &password, const std::string &challenge)
{
/* This is a simple (maybe a bit hacky?) HMAC algorithm, thanks to jilles for
* suggesting the use of HMAC to secure the password against various attacks.
*
* Note: If m_sha256.so is not loaded, we MUST fall back to plaintext with no
* HMAC challenge/response.
*/
Module* sha256 = Instance->FindModule("m_sha256.so");
if (Utils->ChallengeResponse && sha256 && !challenge.empty())
{
/* XXX: This is how HMAC is supposed to be done:
*
* sha256( (pass xor 0x5c) + sha256((pass xor 0x36) + m) )
*
* Note that we are encoding the hex hash, not the binary
* output of the hash which is slightly different to standard.
*
* Don't ask me why its always 0x5c and 0x36... it just is.
*/
std::string hmac1, hmac2;
for (size_t n = 0; n < password.length(); n++)
{
hmac1 += static_cast<char>(password[n] ^ 0x5C);
hmac2 += static_cast<char>(password[n] ^ 0x36);
}
hmac2 += challenge;
HashResetRequest(Utils->Creator, sha256).Send();
hmac2 = HashSumRequest(Utils->Creator, sha256, hmac2).Send();
HashResetRequest(Utils->Creator, sha256).Send();
std::string hmac = hmac1 + hmac2;
hmac = HashSumRequest(Utils->Creator, sha256, hmac).Send();
return "HMAC-SHA256:"+ hmac;
}
else if (!challenge.empty() && !sha256)
Instance->Log(DEFAULT,"Not authenticating to server using SHA256/HMAC because we don't have m_sha256 loaded!");
return password;
}
/** When an outbound connection finishes connecting, we receive
* this event, and must send our SERVER string to the other
* side. If the other side is happy, as outlined in the server
* to server docs on the inspircd.org site, the other side
* will then send back its own server string.
*/
bool TreeSocket::OnConnected()
{
if (this->LinkState == CONNECTING)
{
/* we do not need to change state here. */
for (std::vector<Link>::iterator x = Utils->LinkBlocks.begin(); x < Utils->LinkBlocks.end(); x++)
{
if (x->Name == this->myhost)
{
this->Instance->SNO->WriteToSnoMask('l',"Connection to \2"+myhost+"\2["+(x->HiddenFromStats ? "<hidden>" : this->GetIP())+"] started.");
if (Hook)
{
InspSocketHookRequest(this, (Module*)Utils->Creator, Hook).Send();
this->Instance->SNO->WriteToSnoMask('l',"Connection to \2"+myhost+"\2["+(x->HiddenFromStats ? "<hidden>" : this->GetIP())+"] using transport \2"+x->Hook+"\2");
}
this->OutboundPass = x->SendPass;
sentcapab = false;
/* found who we're supposed to be connecting to, send the neccessary gubbins. */
if (this->GetHook())
Instance->Timers->AddTimer(new HandshakeTimer(Instance, this, &(*x), this->Utils, 1));
else
this->SendCapabilities();
return true;
}
}
}
/* There is a (remote) chance that between the /CONNECT and the connection
* being accepted, some muppet has removed the <link> block and rehashed.
* If that happens the connection hangs here until it's closed. Unlikely
* and rather harmless.
*/
this->Instance->SNO->WriteToSnoMask('l',"Connection to \2"+myhost+"\2 lost link tag(!)");
return true;
}
void TreeSocket::OnError(InspSocketError e)
{
Link* MyLink;
switch (e)
{
case I_ERR_CONNECT:
this->Instance->SNO->WriteToSnoMask('l',"Connection failed: Connection to \002"+myhost+"\002 refused");
MyLink = Utils->FindLink(myhost);
if (MyLink)
Utils->DoFailOver(MyLink);
break;
case I_ERR_SOCKET:
this->Instance->SNO->WriteToSnoMask('l',"Connection failed: Could not create socket");
break;
case I_ERR_BIND:
this->Instance->SNO->WriteToSnoMask('l',"Connection failed: Error binding socket to address or port");
break;
case I_ERR_WRITE:
this->Instance->SNO->WriteToSnoMask('l',"Connection failed: I/O error on connection");
break;
case I_ERR_NOMOREFDS:
this->Instance->SNO->WriteToSnoMask('l',"Connection failed: Operating system is out of file descriptors!");
break;
default:
if ((errno) && (errno != EINPROGRESS) && (errno != EAGAIN))
{
std::string errstr = strerror(errno);
this->Instance->SNO->WriteToSnoMask('l',"Connection to \002"+myhost+"\002 failed with OS error: " + errstr);
}
break;
}
}
int TreeSocket::OnDisconnect()
{
/* For the same reason as above, we don't
* handle OnDisconnect()
*/
return true;
}
/** Recursively send the server tree with distances as hops.
* This is used during network burst to inform the other server
* (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 hopcounts given
* by this function are relative, this doesn't matter so long as
* they are all >1, as all the remote servers re-calculate them
* to be relative too, with themselves as hop 0.
*/
void TreeSocket::SendServers(TreeServer* Current, TreeServer* s, int hops)
{
char command[1024];
for (unsigned int q = 0; q < Current->ChildCount(); q++)
{
TreeServer* recursive_server = Current->GetChild(q);
if (recursive_server != s)
{
snprintf(command,1024,":%s SERVER %s * %d :%s",Current->GetName().c_str(),recursive_server->GetName().c_str(),hops,recursive_server->GetDesc().c_str());
this->WriteLine(command);
this->WriteLine(":"+recursive_server->GetName()+" VERSION :"+recursive_server->GetVersion());
/* down to next level */
this->SendServers(recursive_server, s, hops+1);
}
}
}
std::string TreeSocket::MyCapabilities()
{
std::vector<std::string> modlist;
std::string capabilities;
for (int i = 0; i <= this->Instance->GetModuleCount(); i++)
{
if (this->Instance->modules[i]->GetVersion().Flags & VF_COMMON)
modlist.push_back(this->Instance->Config->module_names[i]);
}
sort(modlist.begin(),modlist.end());
for (unsigned int i = 0; i < modlist.size(); i++)
{
if (i)
capabilities = capabilities + ",";
capabilities = capabilities + modlist[i];
}
return capabilities;
}
std::string TreeSocket::RandString(unsigned int length)
{
char* randombuf = new char[length+1];
std::string out;
#ifdef WINDOWS
int fd = -1;
#else
int fd = open("/dev/urandom", O_RDONLY, 0);
#endif
if (fd >= 0)
{
#ifndef WINDOWS
read(fd, randombuf, length);
close(fd);
#endif
}
else
{
for (unsigned int i = 0; i < length; i++)
randombuf[i] = rand();
}
for (unsigned int i = 0; i < length; i++)
{
char randchar = static_cast<char>((randombuf[i] & 0x7F) | 0x21);
out += (randchar == '=' ? '_' : randchar);
}
delete[] randombuf;
return out;
}
void TreeSocket::SendCapabilities()
{
if (sentcapab)
return;
sentcapab = true;
irc::commasepstream modulelist(MyCapabilities());
this->WriteLine("CAPAB START");
/* Send module names, split at 509 length */
std::string item = "*";
std::string line = "CAPAB MODULES ";
while ((item = modulelist.GetToken()) != "")
{
if (line.length() + item.length() + 1 > 509)
{
this->WriteLine(line);
line = "CAPAB MODULES ";
}
if (line != "CAPAB MODULES ")
line.append(",");
line.append(item);
}
if (line != "CAPAB MODULES ")
this->WriteLine(line);
int ip6 = 0;
int ip6support = 0;
#ifdef IPV6
ip6 = 1;
#endif
#ifdef SUPPORT_IP6LINKS
ip6support = 1;
#endif
std::string extra;
/* Do we have sha256 available? If so, we send a challenge */
if (Utils->ChallengeResponse && (Instance->FindModule("m_sha256.so")))
{
this->SetOurChallenge(RandString(20));
extra = " CHALLENGE=" + this->GetOurChallenge();
}
this->WriteLine("CAPAB CAPABILITIES :NICKMAX="+ConvToStr(NICKMAX)+" HALFOP="+ConvToStr(this->Instance->Config->AllowHalfop)+" CHANMAX="+ConvToStr(CHANMAX)+" MAXMODES="+ConvToStr(MAXMODES)+" IDENTMAX="+ConvToStr(IDENTMAX)+" MAXQUIT="+ConvToStr(MAXQUIT)+" MAXTOPIC="+ConvToStr(MAXTOPIC)+" MAXKICK="+ConvToStr(MAXKICK)+" MAXGECOS="+ConvToStr(MAXGECOS)+" MAXAWAY="+ConvToStr(MAXAWAY)+" IP6NATIVE="+ConvToStr(ip6)+" IP6SUPPORT="+ConvToStr(ip6support)+" PROTOCOL="+ConvToStr(ProtocolVersion)+extra+" PREFIX="+Instance->Modes->BuildPrefixes()+" CHANMODES="+Instance->Modes->ChanModes());
this->WriteLine("CAPAB END");
}
/* Check a comma seperated list for an item */
bool TreeSocket::HasItem(const std::string &list, const std::string &item)
{
irc::commasepstream seplist(list);
std::string item2 = "*";
while ((item2 = seplist.GetToken()) != "")
{
if (item2 == item)
return true;
}
return false;
}
/* Isolate and return the elements that are different between two comma seperated lists */
std::string TreeSocket::ListDifference(const std::string &one, const std::string &two)
{
irc::commasepstream list_one(one);
std::string item = "*";
std::string result;
while ((item = list_one.GetToken()) != "")
{
if (!HasItem(two, item))
{
result.append(" ");
result.append(item);
}
}
return result;
}
void TreeSocket::SendError(const std::string &errormessage)
{
/* Display the error locally as well as sending it remotely */
this->WriteLine("ERROR :"+errormessage);
this->Instance->SNO->WriteToSnoMask('l',"Sent \2ERROR\2 to "+this->InboundServerName+": "+errormessage);
/* One last attempt to make sure the error reaches its target */
this->FlushWriteBuffer();
}
bool TreeSocket::Capab(const std::deque<std::string> ¶ms)
{
if (params.size() < 1)
{
this->SendError("Invalid number of parameters for CAPAB - Mismatched version");
return false;
}
if (params[0] == "START")
{
this->ModuleList.clear();
this->CapKeys.clear();
}
else if (params[0] == "END")
{
std::string reason;
int ip6support = 0;
#ifdef SUPPORT_IP6LINKS
ip6support = 1;
#endif
/* Compare ModuleList and check CapKeys...
* Maybe this could be tidier? -- Brain
*/
if ((this->ModuleList != this->MyCapabilities()) && (this->ModuleList.length()))
{
std::string diff = ListDifference(this->ModuleList, this->MyCapabilities());
if (!diff.length())
{
diff = "your server:" + ListDifference(this->MyCapabilities(), this->ModuleList);
}
else
{
diff = "this server:" + diff;
}
if (diff.length() == 12)
reason = "Module list in CAPAB is not alphabetically ordered, cannot compare lists.";
else
reason = "Modules loaded on these servers are not correctly matched, these modules are not loaded on " + diff;
}
cap_validation valid_capab[] = {
{"Maximum nickname lengths differ or remote nickname length not specified", "NICKMAX", NICKMAX},
{"Maximum ident lengths differ or remote ident length not specified", "IDENTMAX", IDENTMAX},
{"Maximum channel lengths differ or remote channel length not specified", "CHANMAX", CHANMAX},
{"Maximum modes per line differ or remote modes per line not specified", "MAXMODES", MAXMODES},
{"Maximum quit lengths differ or remote quit length not specified", "MAXQUIT", MAXQUIT},
{"Maximum topic lengths differ or remote topic length not specified", "MAXTOPIC", MAXTOPIC},
{"Maximum kick lengths differ or remote kick length not specified", "MAXKICK", MAXKICK},
{"Maximum GECOS (fullname) lengths differ or remote GECOS length not specified", "MAXGECOS", MAXGECOS},
{"Maximum awaymessage lengths differ or remote awaymessage length not specified", "MAXAWAY", MAXAWAY},
{"", "", 0}
};
if (((this->CapKeys.find("IP6SUPPORT") == this->CapKeys.end()) && (ip6support)) || ((this->CapKeys.find("IP6SUPPORT") != this->CapKeys.end()) && (this->CapKeys.find("IP6SUPPORT")->second != ConvToStr(ip6support))))
reason = "We don't both support linking to IPV6 servers";
if (((this->CapKeys.find("IP6NATIVE") != this->CapKeys.end()) && (this->CapKeys.find("IP6NATIVE")->second == "1")) && (!ip6support))
reason = "The remote server is IPV6 native, and we don't support linking to IPV6 servers";
if (((this->CapKeys.find("PROTOCOL") == this->CapKeys.end()) || ((this->CapKeys.find("PROTOCOL") != this->CapKeys.end()) && (this->CapKeys.find("PROTOCOL")->second != ConvToStr(ProtocolVersion)))))
{
if (this->CapKeys.find("PROTOCOL") != this->CapKeys.end())
reason = "Mismatched protocol versions "+this->CapKeys.find("PROTOCOL")->second+" and "+ConvToStr(ProtocolVersion);
else
reason = "Protocol version not specified";
}
if(this->CapKeys.find("PREFIX") != this->CapKeys.end() && this->CapKeys.find("PREFIX")->second != this->Instance->Modes->BuildPrefixes())
reason = "One or more of the prefixes on the remote server are invalid on this server.";
if (((this->CapKeys.find("HALFOP") == this->CapKeys.end()) && (Instance->Config->AllowHalfop)) || ((this->CapKeys.find("HALFOP") != this->CapKeys.end()) && (this->CapKeys.find("HALFOP")->second != ConvToStr(Instance->Config->AllowHalfop))))
reason = "We don't both have halfop support enabled/disabled identically";
for (int x = 0; valid_capab[x].size; ++x)
{
if (((this->CapKeys.find(valid_capab[x].key) == this->CapKeys.end()) || ((this->CapKeys.find(valid_capab[x].key) != this->CapKeys.end()) &&
(this->CapKeys.find(valid_capab[x].key)->second != ConvToStr(valid_capab[x].size)))))
reason = valid_capab[x].reason;
}
/* Challenge response, store their challenge for our password */
std::map<std::string,std::string>::iterator n = this->CapKeys.find("CHALLENGE");
if (Utils->ChallengeResponse && (n != this->CapKeys.end()) && (Instance->FindModule("m_sha256.so")))
{
/* Challenge-response is on now */
this->SetTheirChallenge(n->second);
if (!this->GetTheirChallenge().empty() && (this->LinkState == CONNECTING))
{
this->WriteLine(std::string("SERVER ")+this->Instance->Config->ServerName+" "+this->MakePass(OutboundPass, this->GetTheirChallenge())+" 0 :"+this->Instance->Config->ServerDesc);
}
}
else
{
/* They didnt specify a challenge or we don't have m_sha256.so, we use plaintext */
if (this->LinkState == CONNECTING)
this->WriteLine(std::string("SERVER ")+this->Instance->Config->ServerName+" "+OutboundPass+" 0 :"+this->Instance->Config->ServerDesc);
}
if (reason.length())
{
this->SendError("CAPAB negotiation failed: "+reason);
return false;
}
}
else if ((params[0] == "MODULES") && (params.size() == 2))
{
if (!this->ModuleList.length())
{
this->ModuleList.append(params[1]);
}
else
{
this->ModuleList.append(",");
this->ModuleList.append(params[1]);
}
}
else if ((params[0] == "CAPABILITIES") && (params.size() == 2))
{
irc::tokenstream capabs(params[1]);
std::string item;
bool more = true;
while ((more = capabs.GetToken(item)))
{
/* Process each key/value pair */
std::string::size_type equals = item.rfind('=');
if (equals != std::string::npos)
{
std::string var = item.substr(0, equals);
std::string value = item.substr(equals+1, item.length());
CapKeys[var] = value;
}
}
}
return true;
}
/** This function forces this server to quit, removing this server
* and any users on it (and servers and users below that, etc etc).
* It's very slow and pretty clunky, but luckily unless your network
* is having a REAL bad hair day, this function shouldnt be called
* too many times a month ;-)
*/
void TreeSocket::SquitServer(std::string &from, TreeServer* Current)
{
/* recursively squit the servers attached to 'Current'.
* We're going backwards so we don't remove users
* while we still need them ;)
*/
for (unsigned int q = 0; q < Current->ChildCount(); q++)
{
TreeServer* recursive_server = Current->GetChild(q);
this->SquitServer(from,recursive_server);
}
/* Now we've whacked the kids, whack self */
num_lost_servers++;
num_lost_users += Current->QuitUsers(from);
}
/** This is a wrapper function for SquitServer above, which
* does some validation first and passes on the SQUIT to all
* other remaining servers.
*/
void TreeSocket::Squit(TreeServer* Current, const std::string &reason)
{
if ((Current) && (Current != Utils->TreeRoot))
{
Event rmode((char*)Current->GetName().c_str(), (Module*)Utils->Creator, "lost_server");
rmode.Send(Instance);
std::deque<std::string> params;
params.push_back(Current->GetName());
params.push_back(":"+reason);
Utils->DoOneToAllButSender(Current->GetParent()->GetName(),"SQUIT",params,Current->GetName());
if (Current->GetParent() == Utils->TreeRoot)
{
this->Instance->SNO->WriteToSnoMask('l',"Server \002"+Current->GetName()+"\002 split: "+reason);
}
else
{
this->Instance->SNO->WriteToSnoMask('l',"Server \002"+Current->GetName()+"\002 split from server \002"+Current->GetParent()->GetName()+"\002 with reason: "+reason);
}
num_lost_servers = 0;
num_lost_users = 0;
std::string from = Current->GetParent()->GetName()+" "+Current->GetName();
SquitServer(from, Current);
Current->Tidy();
Current->GetParent()->DelChild(Current);
DELETE(Current);
this->Instance->SNO->WriteToSnoMask('l',"Netsplit complete, lost \002%d\002 users on \002%d\002 servers.", num_lost_users, num_lost_servers);
}
else
Instance->Log(DEFAULT,"Squit from unknown server");
}
/** FMODE command - server mode with timestamp checks */
bool TreeSocket::ForceMode(const std::string &source, std::deque<std::string> ¶ms)
{
/* Chances are this is a 1.0 FMODE without TS */
if (params.size() < 3)
{
/* No modes were in the command, probably a channel with no modes set on it */
return true;
}
bool smode = false;
std::string sourceserv;
/* Are we dealing with an FMODE from a user, or from a server? */
userrec* who = this->Instance->FindNick(source);
if (who)
{
/* FMODE from a user, set sourceserv to the users server name */
sourceserv = who->server;
}
else
{
/* FMODE from a server, create a fake user to receive mode feedback */
who = new userrec(this->Instance);
who->SetFd(FD_MAGIC_NUMBER);
smode = true; /* Setting this flag tells us we should free the userrec later */
sourceserv = source; /* Set sourceserv to the actual source string */
}
const char* modelist[64];
time_t TS = 0;
int n = 0;
memset(&modelist,0,sizeof(modelist));
for (unsigned int q = 0; (q < params.size()) && (q < 64); q++)
{
if (q == 1)
{
/* The timestamp is in this position.
* We don't want to pass that up to the
* server->client protocol!
*/
TS = atoi(params[q].c_str());
}
else
{
/* Everything else is fine to append to the modelist */
modelist[n++] = params[q].c_str();
}
}
/* Extract the TS value of the object, either userrec or chanrec */
userrec* dst = this->Instance->FindNick(params[0]);
chanrec* chan = NULL;
time_t ourTS = 0;
if (dst)
{
ourTS = dst->age;
}
else
{
chan = this->Instance->FindChan(params[0]);
if (chan)
{
ourTS = chan->age;
}
else
/* Oops, channel doesnt exist! */
return true;
}
if (!TS)
{
Instance->Log(DEFAULT,"*** BUG? *** TS of 0 sent to FMODE. Are some services authors smoking craq, or is it 1970 again?. Dropped.");
Instance->SNO->WriteToSnoMask('d', "WARNING: The server %s is sending FMODE with a TS of zero. Total craq. Mode was dropped.", sourceserv.c_str());
return true;
}
/* TS is equal or less: Merge the mode changes into ours and pass on.
*/
if (TS <= ourTS)
{
if ((TS < ourTS) && (!dst))
Instance->Log(DEFAULT,"*** BUG *** Channel TS sent in FMODE to %s is %lu which is not equal to %lu!", params[0].c_str(), TS, ourTS);
if (smode)
{
this->Instance->SendMode(modelist, n, who);
}
else
{
this->Instance->CallCommandHandler("MODE", modelist, n, who);
}
/* HOT POTATO! PASS IT ON! */
Utils->DoOneToAllButSender(source,"FMODE",params,sourceserv);
}
/* If the TS is greater than ours, we drop the mode and dont pass it anywhere.
*/
if (smode)
DELETE(who);
return true;
}
/** FTOPIC command */
bool TreeSocket::ForceTopic(const std::string &source, std::deque<std::string> ¶ms)
{
if (params.size() != 4)
return true;
time_t ts = atoi(params[1].c_str());
std::string nsource = source;
chanrec* c = this->Instance->FindChan(params[0]);
if (c)
{
if ((ts >= c->topicset) || (!*c->topic))
{
std::string oldtopic = c->topic;
strlcpy(c->topic,params[3].c_str(),MAXTOPIC);
strlcpy(c->setby,params[2].c_str(),127);
c->topicset = ts;
/* if the topic text is the same as the current topic,
* dont bother to send the TOPIC command out, just silently
* update the set time and set nick.
*/
if (oldtopic != params[3])
{
userrec* user = this->Instance->FindNick(source);
if (!user)
{
c->WriteChannelWithServ(Instance->Config->ServerName, "TOPIC %s :%s", c->name, c->topic);
}
else
{
c->WriteChannel(user, "TOPIC %s :%s", c->name, c->topic);
nsource = user->server;
}
/* all done, send it on its way */
params[3] = ":" + params[3];
Utils->DoOneToAllButSender(source,"FTOPIC",params,nsource);
}
}
}
return true;
}
/** FJOIN, similar to TS6 SJOIN, but not quite. */
bool TreeSocket::ForceJoin(const std::string &source, std::deque<std::string> ¶ms)
{
/* 1.1 FJOIN works as follows:
*
* Each FJOIN is sent along with a timestamp, and the side with the lowest
* timestamp 'wins'. From this point on we will refer to this side as the
* winner. The side with the higher timestamp loses, from this point on we
* will call this side the loser or losing side. This should be familiar to
* anyone who's dealt with dreamforge or TS6 before.
*
* When two sides of a split heal and this occurs, the following things
* will happen:
*
* If the timestamps are exactly equal, both sides merge their privilages
* and users, as in InspIRCd 1.0 and ircd2.8. The channels have not been
* re-created during a split, this is safe to do.
*
* If the timestamps are NOT equal, the losing side removes all of its
* modes from the channel, before introducing new users into the channel
* which are listed in the FJOIN command's parameters. The losing side then
* LOWERS its timestamp value of the channel to match that of the winning
* side, and the modes of the users of the winning side are merged in with
* the losing side.
*
* The winning side on the other hand will ignore all user modes from the
* losing side, so only its own modes get applied. Life is simple for those
* who succeed at internets. :-)
*
* NOTE: Unlike TS6 and dreamforge and other protocols which have SJOIN,
* FJOIN does not contain the simple-modes such as +iklmnsp. Why not,
* you ask? Well, quite simply because we don't need to. They'll be sent
* after the FJOIN by FMODE, and FMODE is timestamped, so in the event
* the losing side sends any modes for the channel which shouldnt win,
* they wont as their timestamp will be too high :-)
*/
if (params.size() < 3)
return true;
irc::modestacker modestack(true); /* Modes to apply from the users in the user list */
userrec* who = NULL; /* User we are currently checking */
std::string channel = params[0]; /* Channel name, as a string */
time_t TS = atoi(params[1].c_str()); /* Timestamp given to us for remote side */
irc::tokenstream users(params[2]); /* Users from the user list */
bool apply_other_sides_modes = true; /* True if we are accepting the other side's modes */
chanrec* chan = this->Instance->FindChan(channel); /* The channel we're sending joins to */
time_t ourTS = chan ? chan->age : Instance->Time(true)+600; /* The TS of our side of the link */
bool created = !chan; /* True if the channel doesnt exist here yet */
std::string item; /* One item in the list of nicks */
params[2] = ":" + params[2];
Utils->DoOneToAllButSender(source,"FJOIN",params,source);
if (!TS)
{
Instance->Log(DEFAULT,"*** BUG? *** TS of 0 sent to FJOIN. Are some services authors smoking craq, or is it 1970 again?. Dropped.");
Instance->SNO->WriteToSnoMask('d', "WARNING: The server %s is sending FJOIN with a TS of zero. Total craq. Command was dropped.", source.c_str());
return true;
}
/* If our TS is less than theirs, we dont accept their modes */
if (ourTS < TS)
apply_other_sides_modes = false;
/* Our TS greater than theirs, clear all our modes from the channel, accept theirs. */
if (ourTS > TS)
{
std::deque<std::string> param_list;
if (Utils->AnnounceTSChange && chan)
chan->WriteChannelWithServ(Instance->Config->ServerName, "NOTICE %s :TS for %s changed from %lu to %lu", chan->name, chan->name, ourTS, TS);
ourTS = TS;
if (!created)
{
chan->age = TS;
param_list.push_back(channel);
this->RemoveStatus(Instance->Config->ServerName, param_list);
}
}
/* Now, process every 'prefixes,nick' pair */
while (users.GetToken(item))
{
const char* usr = item.c_str();
if (usr && *usr)
{
const char* permissions = usr;
/* Iterate through all the prefix values, convert them from prefixes to mode letters */
std::string modes;
while ((*permissions) && (*permissions != ','))
{
ModeHandler* mh = Instance->Modes->FindPrefix(*permissions);
if (mh)
modes = modes + mh->GetModeChar();
else
{
this->SendError(std::string("Invalid prefix '")+(*permissions)+"' in FJOIN");
return false;
}
usr++;
permissions++;
}
/* Advance past the comma, to the nick */
usr++;
/* Check the user actually exists */
who = this->Instance->FindNick(usr);
if (who)
{
/* Check that the user's 'direction' is correct */
TreeServer* route_back_again = Utils->BestRouteTo(who->server);
if ((!route_back_again) || (route_back_again->GetSocket() != this))
continue;
/* Add any permissions this user had to the mode stack */
for (std::string::iterator x = modes.begin(); x != modes.end(); ++x)
modestack.Push(*x, who->nick);
chanrec::JoinUser(this->Instance, who, channel.c_str(), true, "", TS);
}
else
{
Instance->Log(SPARSE,"Warning! Invalid user %s in FJOIN to channel %s IGNORED", usr, channel.c_str());
continue;
}
}
}
/* Flush mode stacker if we lost the FJOIN or had equal TS */
if (apply_other_sides_modes)
{
std::deque<std::string> stackresult;
const char* mode_junk[MAXMODES+2];
userrec* n = new userrec(Instance);
n->SetFd(FD_MAGIC_NUMBER);
mode_junk[0] = channel.c_str();
while (modestack.GetStackedLine(stackresult))
{
for (size_t j = 0; j < stackresult.size(); j++)
{
mode_junk[j+1] = stackresult[j].c_str();
}
Instance->SendMode(mode_junk, stackresult.size() + 1, n);
}
delete n;
}
return true;
}
/** NICK command */
bool TreeSocket::IntroduceClient(const std::string &source, std::deque<std::string> ¶ms)
{
/** Do we have enough parameters:
* NICK age nick host dhost ident +modes ip :gecos
*/
if (params.size() != 8)
{
this->WriteLine(std::string(":")+this->Instance->Config->ServerName+" KILL "+params[1]+" :Invalid client introduction ("+params[1]+"?)");
return true;
}
time_t age = ConvToInt(params[0]);
const char* tempnick = params[1].c_str();
cmd_validation valid[] = { {"Nickname", 1, NICKMAX}, {"Hostname", 2, 64}, {"Displayed hostname", 3, 64}, {"Ident", 4, IDENTMAX}, {"GECOS", 7, MAXGECOS}, {"", 0, 0} };
TreeServer* remoteserver = Utils->FindServer(source);
if (!remoteserver)
{
this->WriteLine(std::string(":")+this->Instance->Config->ServerName+" KILL "+params[1]+" :Invalid client introduction (Unknown server "+source+")");
return true;
}
/* Check parameters for validity before introducing the client, discovered by dmb */
if (!age)
{
this->WriteLine(std::string(":")+this->Instance->Config->ServerName+" KILL "+params[1]+" :Invalid client introduction (Invalid TS?)");
return true;
}
for (size_t x = 0; valid[x].length; ++x)
{
if (params[valid[x].param].length() > valid[x].length)
{
this->WriteLine(std::string(":")+this->Instance->Config->ServerName+" KILL "+params[1]+" :Invalid client introduction (" + valid[x].item + " > " + ConvToStr(valid[x].length) + ")");
return true;
}
}
/** Our client looks ok, lets introduce it now
*/
Instance->Log(DEBUG,"New remote client %s",tempnick);
user_hash::iterator iter = this->Instance->clientlist->find(tempnick);
if (iter != this->Instance->clientlist->end())
{
/* nick collision */
this->WriteLine(std::string(":")+this->Instance->Config->ServerName+" KILL "+tempnick+" :Nickname collision");
userrec::QuitUser(this->Instance, iter->second, "Nickname collision");
return true;
}
userrec* _new = new userrec(this->Instance);
(*(this->Instance->clientlist))[tempnick] = _new;
_new->SetFd(FD_MAGIC_NUMBER);
strlcpy(_new->nick, tempnick,NICKMAX-1);
strlcpy(_new->host, params[2].c_str(),64);
strlcpy(_new->dhost, params[3].c_str(),64);
_new->server = this->Instance->FindServerNamePtr(source.c_str());
strlcpy(_new->ident, params[4].c_str(),IDENTMAX);
strlcpy(_new->fullname, params[7].c_str(),MAXGECOS);
_new->registered = REG_ALL;
_new->signon = age;
/* we need to remove the + from the modestring, so we can do our stuff */
std::string::size_type pos_after_plus = params[5].find_first_not_of('+');
if (pos_after_plus != std::string::npos)
params[5] = params[5].substr(pos_after_plus);
for (std::string::iterator v = params[5].begin(); v != params[5].end(); v++)
{
_new->modes[(*v)-65] = 1;
/* For each mode thats set, increase counter */
ModeHandler* mh = Instance->Modes->FindMode(*v, MODETYPE_USER);
if (mh)
mh->ChangeCount(1);
}
/* now we've done with modes processing, put the + back for remote servers */
params[5] = "+" + params[5];
#ifdef SUPPORT_IP6LINKS
if (params[6].find_first_of(":") != std::string::npos)
_new->SetSockAddr(AF_INET6, params[6].c_str(), 0);
else
#endif
_new->SetSockAddr(AF_INET, params[6].c_str(), 0);
Instance->AddGlobalClone(_new);
bool dosend = !(((this->Utils->quiet_bursts) && (this->bursting || Utils->FindRemoteBurstServer(remoteserver))) || (this->Instance->SilentULine(_new->server)));
if (dosend)
this->Instance->SNO->WriteToSnoMask('C',"Client connecting at %s: %s!%s@%s [%s] [%s]",_new->server,_new->nick,_new->ident,_new->host, _new->GetIPString(), _new->fullname);
params[7] = ":" + params[7];
Utils->DoOneToAllButSender(source,"NICK", params, source);
// Increment the Source Servers User Count..
TreeServer* SourceServer = Utils->FindServer(source);
if (SourceServer)
{
SourceServer->AddUserCount();
}
FOREACH_MOD_I(Instance,I_OnPostConnect,OnPostConnect(_new));
return true;
}
/** 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.
*/
void TreeSocket::SendFJoins(TreeServer* Current, chanrec* c)
{
std::string buffer;
char list[MAXBUF];
std::string individual_halfops = std::string(":")+this->Instance->Config->ServerName+" FMODE "+c->name+" "+ConvToStr(c->age);
size_t dlen, curlen;
dlen = curlen = snprintf(list,MAXBUF,":%s FJOIN %s %lu",this->Instance->Config->ServerName,c->name,(unsigned long)c->age);
int numusers = 0;
char* ptr = list + dlen;
CUList *ulist = c->GetUsers();
std::string modes;
std::string params;
for (CUList::iterator i = ulist->begin(); i != ulist->end(); i++)
{
// The first parameter gets a : before it
size_t ptrlen = snprintf(ptr, MAXBUF, " %s%s,%s", !numusers ? ":" : "", c->GetAllPrefixChars(i->first), i->first->nick);
curlen += ptrlen;
ptr += ptrlen;
numusers++;
if (curlen > (480-NICKMAX))
{
buffer.append(list).append("\r\n");
dlen = curlen = snprintf(list,MAXBUF,":%s FJOIN %s %lu",this->Instance->Config->ServerName,c->name,(unsigned long)c->age);
ptr = list + dlen;
ptrlen = 0;
numusers = 0;
}
}
if (numusers)
buffer.append(list).append("\r\n");
buffer.append(":").append(this->Instance->Config->ServerName).append(" FMODE ").append(c->name).append(" ").append(ConvToStr(c->age)).append(" +").append(c->ChanModes(true)).append("\r\n");
int linesize = 1;
for (BanList::iterator b = c->bans.begin(); b != c->bans.end(); b++)
{
int size = strlen(b->data) + 2;
int currsize = linesize + size;
if (currsize <= 350)
{
modes.append("b");
params.append(" ").append(b->data);
linesize += size;
}
if ((params.length() >= MAXMODES) || (currsize > 350))
{
/* Wrap at MAXMODES */
buffer.append(":").append(this->Instance->Config->ServerName).append(" FMODE ").append(c->name).append(" ").append(ConvToStr(c->age)).append(" +").append(modes).append(params).append("\r\n");
modes.clear();
params.clear();
linesize = 1;
}
}
/* Only send these if there are any */
if (!modes.empty())
buffer.append(":").append(this->Instance->Config->ServerName).append(" FMODE ").append(c->name).append(" ").append(ConvToStr(c->age)).append(" +").append(modes).append(params);
this->WriteLine(buffer);
}
/** Send G, Q, Z and E lines */
void TreeSocket::SendXLines(TreeServer* Current)
{
char data[MAXBUF];
std::string buffer;
std::string n = this->Instance->Config->ServerName;
const char* sn = n.c_str();
/* Yes, these arent too nice looking, but they get the job done */
for (std::vector<ZLine*>::iterator i = Instance->XLines->zlines.begin(); i != Instance->XLines->zlines.end(); i++)
{
snprintf(data,MAXBUF,":%s ADDLINE Z %s %s %lu %lu :%s\r\n",sn,(*i)->ipaddr,(*i)->source,(unsigned long)(*i)->set_time,(unsigned long)(*i)->duration,(*i)->reason);
buffer.append(data);
}
for (std::vector<QLine*>::iterator i = Instance->XLines->qlines.begin(); i != Instance->XLines->qlines.end(); i++)
{
snprintf(data,MAXBUF,":%s ADDLINE Q %s %s %lu %lu :%s\r\n",sn,(*i)->nick,(*i)->source,(unsigned long)(*i)->set_time,(unsigned long)(*i)->duration,(*i)->reason);
buffer.append(data);
}
for (std::vector<GLine*>::iterator i = Instance->XLines->glines.begin(); i != Instance->XLines->glines.end(); i++)
{
snprintf(data,MAXBUF,":%s ADDLINE G %s@%s %s %lu %lu :%s\r\n",sn,(*i)->identmask,(*i)->hostmask,(*i)->source,(unsigned long)(*i)->set_time,(unsigned long)(*i)->duration,(*i)->reason);
buffer.append(data);
}
for (std::vector<ELine*>::iterator i = Instance->XLines->elines.begin(); i != Instance->XLines->elines.end(); i++)
{
snprintf(data,MAXBUF,":%s ADDLINE E %s@%s %s %lu %lu :%s\r\n",sn,(*i)->identmask,(*i)->hostmask,(*i)->source,(unsigned long)(*i)->set_time,(unsigned long)(*i)->duration,(*i)->reason);
buffer.append(data);
}
for (std::vector<ZLine*>::iterator i = Instance->XLines->pzlines.begin(); i != Instance->XLines->pzlines.end(); i++)
{
snprintf(data,MAXBUF,":%s ADDLINE Z %s %s %lu %lu :%s\r\n",sn,(*i)->ipaddr,(*i)->source,(unsigned long)(*i)->set_time,(unsigned long)(*i)->duration,(*i)->reason);
buffer.append(data);
}
for (std::vector<QLine*>::iterator i = Instance->XLines->pqlines.begin(); i != Instance->XLines->pqlines.end(); i++)
{
snprintf(data,MAXBUF,":%s ADDLINE Q %s %s %lu %lu :%s\r\n",sn,(*i)->nick,(*i)->source,(unsigned long)(*i)->set_time,(unsigned long)(*i)->duration,(*i)->reason);
buffer.append(data);
}
for (std::vector<GLine*>::iterator i = Instance->XLines->pglines.begin(); i != Instance->XLines->pglines.end(); i++)
{
snprintf(data,MAXBUF,":%s ADDLINE G %s@%s %s %lu %lu :%s\r\n",sn,(*i)->identmask,(*i)->hostmask,(*i)->source,(unsigned long)(*i)->set_time,(unsigned long)(*i)->duration,(*i)->reason);
buffer.append(data);
}
for (std::vector<ELine*>::iterator i = Instance->XLines->pelines.begin(); i != Instance->XLines->pelines.end(); i++)
{
snprintf(data,MAXBUF,":%s ADDLINE E %s@%s %s %lu %lu :%s\r\n",sn,(*i)->identmask,(*i)->hostmask,(*i)->source,(unsigned long)(*i)->set_time,(unsigned long)(*i)->duration,(*i)->reason);
buffer.append(data);
}
if (!buffer.empty())
this->WriteLine(buffer);
}
/** Send channel modes and topics */
void TreeSocket::SendChannelModes(TreeServer* Current)
{
char data[MAXBUF];
std::deque<std::string> list;
std::string n = this->Instance->Config->ServerName;
const char* sn = n.c_str();
Instance->Log(DEBUG,"Sending channels and modes, %d to send", this->Instance->chanlist->size());
for (chan_hash::iterator c = this->Instance->chanlist->begin(); c != this->Instance->chanlist->end(); c++)
{
SendFJoins(Current, c->second);
if (*c->second->topic)
{
snprintf(data,MAXBUF,":%s FTOPIC %s %lu %s :%s",sn,c->second->name,(unsigned long)c->second->topicset,c->second->setby,c->second->topic);
this->WriteLine(data);
}
FOREACH_MOD_I(this->Instance,I_OnSyncChannel,OnSyncChannel(c->second,(Module*)Utils->Creator,(void*)this));
list.clear();
c->second->GetExtList(list);
for (unsigned int j = 0; j < list.size(); j++)
{
FOREACH_MOD_I(this->Instance,I_OnSyncChannelMetaData,OnSyncChannelMetaData(c->second,(Module*)Utils->Creator,(void*)this,list[j]));
}
}
}
/** send all users and their oper state/modes */
void TreeSocket::SendUsers(TreeServer* Current)
{
char data[MAXBUF];
std::deque<std::string> list;
std::string dataline;
for (user_hash::iterator u = this->Instance->clientlist->begin(); u != this->Instance->clientlist->end(); u++)
{
if (u->second->registered == REG_ALL)
{
snprintf(data,MAXBUF,":%s NICK %lu %s %s %s %s +%s %s :%s",u->second->server,(unsigned long)u->second->age,u->second->nick,u->second->host,u->second->dhost,u->second->ident,u->second->FormatModes(),u->second->GetIPString(),u->second->fullname);
this->WriteLine(data);
if (*u->second->oper)
{
snprintf(data,MAXBUF,":%s OPERTYPE %s", u->second->nick, u->second->oper);
this->WriteLine(data);
}
if (*u->second->awaymsg)
{
snprintf(data,MAXBUF,":%s AWAY :%s", u->second->nick, u->second->awaymsg);
this->WriteLine(data);
}
}
}
for (user_hash::iterator u = this->Instance->clientlist->begin(); u != this->Instance->clientlist->end(); u++)
{
FOREACH_MOD_I(this->Instance,I_OnSyncUser,OnSyncUser(u->second,(Module*)Utils->Creator,(void*)this));
list.clear();
u->second->GetExtList(list);
for (unsigned int j = 0; j < list.size(); j++)
{
FOREACH_MOD_I(this->Instance,I_OnSyncUserMetaData,OnSyncUserMetaData(u->second,(Module*)Utils->Creator,(void*)this,list[j]));
}
}
}
/** 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
* users require their servers to exist, and channels require their
* users to exist. You get the idea.
*/
void TreeSocket::DoBurst(TreeServer* s)
{
std::string name = s->GetName();
std::string burst = "BURST "+ConvToStr(Instance->Time(true));
std::string endburst = "ENDBURST";
this->Instance->SNO->WriteToSnoMask('l',"Bursting to \2%s\2 (Authentication: %s).", name.c_str(), this->GetTheirChallenge().empty() ? "plaintext password" : "SHA256-HMAC challenge-response");
this->WriteLine(burst);
/* send our version string */
this->WriteLine(std::string(":")+this->Instance->Config->ServerName+" VERSION :"+this->Instance->GetVersionString());
/* Send server tree */
this->SendServers(Utils->TreeRoot,s,1);
/* Send users and their oper status */
this->SendUsers(s);
/* Send everything else (channel modes, xlines etc) */
this->SendChannelModes(s);
this->SendXLines(s);
FOREACH_MOD_I(this->Instance,I_OnSyncOtherMetaData,OnSyncOtherMetaData((Module*)Utils->Creator,(void*)this));
this->WriteLine(endburst);
this->Instance->SNO->WriteToSnoMask('l',"Finished bursting to \2"+name+"\2.");
}
/** This function is called when we receive data from a remote
* server. We buffer the data in a std::string (it doesnt stay
* there for long), reading using InspSocket::Read() which can
* read up to 16 kilobytes in one operation.
*
* IF THIS FUNCTION RETURNS FALSE, THE CORE CLOSES AND DELETES
* THE SOCKET OBJECT FOR US.
*/
bool TreeSocket::OnDataReady()
{
char* data = this->Read();
/* Check that the data read is a valid pointer and it has some content */
if (data && *data)
{
this->in_buffer.append(data);
/* While there is at least one new line in the buffer,
* do something useful (we hope!) with it.
*/
while (in_buffer.find("\n") != std::string::npos)
{
std::string ret = in_buffer.substr(0,in_buffer.find("\n")-1);
in_buffer = in_buffer.substr(in_buffer.find("\n")+1,in_buffer.length()-in_buffer.find("\n"));
/* Use rfind here not find, as theres more
* chance of the \r being near the end of the
* string, not the start.
*/
if (ret.find("\r") != std::string::npos)
ret = in_buffer.substr(0,in_buffer.find("\r")-1);
/* Process this one, abort if it
* didnt return true.
*/
if (!this->ProcessLine(ret))
{
return false;
}
}
return true;
}
/* EAGAIN returns an empty but non-NULL string, so this
* evaluates to TRUE for EAGAIN but to FALSE for EOF.
*/
return (data && !*data);
}
\ No newline at end of file +/* +------------------------------------+ + * | 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 "users.h" +#include "channels.h" +#include "modules.h" +#include "commands/cmd_whois.h" +#include "commands/cmd_stats.h" +#include "socket.h" +#include "wildcard.h" +#include "xline.h" +#include "transport.h" +#include "m_hash.h" +#include "socketengine.h" + +#include "m_spanningtree/main.h" +#include "m_spanningtree/utils.h" +#include "m_spanningtree/treeserver.h" +#include "m_spanningtree/link.h" +#include "m_spanningtree/treesocket.h" +#include "m_spanningtree/resolvers.h" +#include "m_spanningtree/handshaketimer.h" + +/* $ModDep: m_spanningtree/timesynctimer.h m_spanningtree/resolvers.h m_spanningtree/main.h m_spanningtree/utils.h m_spanningtree/treeserver.h m_spanningtree/link.h m_spanningtree/treesocket.h m_hash.h */ + + +/** Because most of the I/O gubbins are encapsulated within + * InspSocket, we just call the superclass constructor for + * most of the action, and append a few of our own values + * to it. + */ +TreeSocket::TreeSocket(SpanningTreeUtilities* Util, InspIRCd* SI, std::string host, int port, bool listening, unsigned long maxtime, Module* HookMod) + : InspSocket(SI, host, port, listening, maxtime), Utils(Util), Hook(HookMod) +{ + myhost = host; + this->LinkState = LISTENER; + theirchallenge.clear(); + ourchallenge.clear(); + if (listening && Hook) + InspSocketHookRequest(this, (Module*)Utils->Creator, Hook).Send(); +} + +TreeSocket::TreeSocket(SpanningTreeUtilities* Util, InspIRCd* SI, std::string host, int port, bool listening, unsigned long maxtime, const std::string &ServerName, const std::string &bindto, Module* HookMod) + : InspSocket(SI, host, port, listening, maxtime, bindto), Utils(Util), Hook(HookMod) +{ + myhost = ServerName; + theirchallenge.clear(); + ourchallenge.clear(); + this->LinkState = CONNECTING; + if (Hook) + InspSocketHookRequest(this, (Module*)Utils->Creator, Hook).Send(); +} + +/** When a listening socket gives us a new file descriptor, + * we must associate it with a socket without creating a new + * connection. This constructor is used for this purpose. + */ +TreeSocket::TreeSocket(SpanningTreeUtilities* Util, InspIRCd* SI, int newfd, char* ip, Module* HookMod) + : InspSocket(SI, newfd, ip), Utils(Util), Hook(HookMod) +{ + this->LinkState = WAIT_AUTH_1; + theirchallenge.clear(); + ourchallenge.clear(); + sentcapab = false; + /* If we have a transport module hooked to the parent, hook the same module to this + * socket, and set a timer waiting for handshake before we send CAPAB etc. + */ + if (Hook) + InspSocketHookRequest(this, (Module*)Utils->Creator, Hook).Send(); + + Instance->Timers->AddTimer(new HandshakeTimer(Instance, this, &(Utils->LinkBlocks[0]), this->Utils, 1)); +} + +ServerState TreeSocket::GetLinkState() +{ + return this->LinkState; +} + +Module* TreeSocket::GetHook() +{ + return this->Hook; +} + +TreeSocket::~TreeSocket() +{ + if (Hook) + InspSocketUnhookRequest(this, (Module*)Utils->Creator, Hook).Send(); + + Utils->DelBurstingServer(this); +} + +const std::string& TreeSocket::GetOurChallenge() +{ + return this->ourchallenge; +} + +void TreeSocket::SetOurChallenge(const std::string &c) +{ + this->ourchallenge = c; +} + +const std::string& TreeSocket::GetTheirChallenge() +{ + return this->theirchallenge; +} + +void TreeSocket::SetTheirChallenge(const std::string &c) +{ + this->theirchallenge = c; +} + +std::string TreeSocket::MakePass(const std::string &password, const std::string &challenge) +{ + /* This is a simple (maybe a bit hacky?) HMAC algorithm, thanks to jilles for + * suggesting the use of HMAC to secure the password against various attacks. + * + * Note: If m_sha256.so is not loaded, we MUST fall back to plaintext with no + * HMAC challenge/response. + */ + Module* sha256 = Instance->FindModule("m_sha256.so"); + if (Utils->ChallengeResponse && sha256 && !challenge.empty()) + { + /* XXX: This is how HMAC is supposed to be done: + * + * sha256( (pass xor 0x5c) + sha256((pass xor 0x36) + m) ) + * + * Note that we are encoding the hex hash, not the binary + * output of the hash which is slightly different to standard. + * + * Don't ask me why its always 0x5c and 0x36... it just is. + */ + std::string hmac1, hmac2; + + for (size_t n = 0; n < password.length(); n++) + { + hmac1 += static_cast<char>(password[n] ^ 0x5C); + hmac2 += static_cast<char>(password[n] ^ 0x36); + } + + hmac2 += challenge; + HashResetRequest(Utils->Creator, sha256).Send(); + hmac2 = HashSumRequest(Utils->Creator, sha256, hmac2).Send(); + + HashResetRequest(Utils->Creator, sha256).Send(); + std::string hmac = hmac1 + hmac2; + hmac = HashSumRequest(Utils->Creator, sha256, hmac).Send(); + + return "HMAC-SHA256:"+ hmac; + } + else if (!challenge.empty() && !sha256) + Instance->Log(DEFAULT,"Not authenticating to server using SHA256/HMAC because we don't have m_sha256 loaded!"); + + return password; +} + +/** When an outbound connection finishes connecting, we receive + * this event, and must send our SERVER string to the other + * side. If the other side is happy, as outlined in the server + * to server docs on the inspircd.org site, the other side + * will then send back its own server string. + */ +bool TreeSocket::OnConnected() +{ + if (this->LinkState == CONNECTING) + { + /* we do not need to change state here. */ + for (std::vector<Link>::iterator x = Utils->LinkBlocks.begin(); x < Utils->LinkBlocks.end(); x++) + { + if (x->Name == this->myhost) + { + this->Instance->SNO->WriteToSnoMask('l',"Connection to \2"+myhost+"\2["+(x->HiddenFromStats ? "<hidden>" : this->GetIP())+"] started."); + if (Hook) + { + InspSocketHookRequest(this, (Module*)Utils->Creator, Hook).Send(); + this->Instance->SNO->WriteToSnoMask('l',"Connection to \2"+myhost+"\2["+(x->HiddenFromStats ? "<hidden>" : this->GetIP())+"] using transport \2"+x->Hook+"\2"); + } + this->OutboundPass = x->SendPass; + sentcapab = false; + + /* found who we're supposed to be connecting to, send the neccessary gubbins. */ + if (this->GetHook()) + Instance->Timers->AddTimer(new HandshakeTimer(Instance, this, &(*x), this->Utils, 1)); + else + this->SendCapabilities(); + + return true; + } + } + } + /* There is a (remote) chance that between the /CONNECT and the connection + * being accepted, some muppet has removed the <link> block and rehashed. + * If that happens the connection hangs here until it's closed. Unlikely + * and rather harmless. + */ + this->Instance->SNO->WriteToSnoMask('l',"Connection to \2"+myhost+"\2 lost link tag(!)"); + return true; +} + +void TreeSocket::OnError(InspSocketError e) +{ + Link* MyLink; + + switch (e) + { + case I_ERR_CONNECT: + this->Instance->SNO->WriteToSnoMask('l',"Connection failed: Connection to \002"+myhost+"\002 refused"); + MyLink = Utils->FindLink(myhost); + if (MyLink) + Utils->DoFailOver(MyLink); + break; + case I_ERR_SOCKET: + this->Instance->SNO->WriteToSnoMask('l',"Connection failed: Could not create socket"); + break; + case I_ERR_BIND: + this->Instance->SNO->WriteToSnoMask('l',"Connection failed: Error binding socket to address or port"); + break; + case I_ERR_WRITE: + this->Instance->SNO->WriteToSnoMask('l',"Connection failed: I/O error on connection"); + break; + case I_ERR_NOMOREFDS: + this->Instance->SNO->WriteToSnoMask('l',"Connection failed: Operating system is out of file descriptors!"); + break; + default: + if ((errno) && (errno != EINPROGRESS) && (errno != EAGAIN)) + { + std::string errstr = strerror(errno); + this->Instance->SNO->WriteToSnoMask('l',"Connection to \002"+myhost+"\002 failed with OS error: " + errstr); + } + break; + } +} + +int TreeSocket::OnDisconnect() +{ + /* For the same reason as above, we don't + * handle OnDisconnect() + */ + return true; +} + +/** Recursively send the server tree with distances as hops. + * This is used during network burst to inform the other server + * (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 hopcounts given + * by this function are relative, this doesn't matter so long as + * they are all >1, as all the remote servers re-calculate them + * to be relative too, with themselves as hop 0. + */ +void TreeSocket::SendServers(TreeServer* Current, TreeServer* s, int hops) +{ + char command[1024]; + for (unsigned int q = 0; q < Current->ChildCount(); q++) + { + TreeServer* recursive_server = Current->GetChild(q); + if (recursive_server != s) + { + snprintf(command,1024,":%s SERVER %s * %d :%s",Current->GetName().c_str(),recursive_server->GetName().c_str(),hops,recursive_server->GetDesc().c_str()); + this->WriteLine(command); + this->WriteLine(":"+recursive_server->GetName()+" VERSION :"+recursive_server->GetVersion()); + /* down to next level */ + this->SendServers(recursive_server, s, hops+1); + } + } +} + +std::string TreeSocket::MyCapabilities() +{ + std::vector<std::string> modlist; + std::string capabilities; + for (int i = 0; i <= this->Instance->GetModuleCount(); i++) + { + if (this->Instance->modules[i]->GetVersion().Flags & VF_COMMON) + modlist.push_back(this->Instance->Config->module_names[i]); + } + sort(modlist.begin(),modlist.end()); + for (unsigned int i = 0; i < modlist.size(); i++) + { + if (i) + capabilities = capabilities + ","; + capabilities = capabilities + modlist[i]; + } + return capabilities; +} + +std::string TreeSocket::RandString(unsigned int length) +{ + char* randombuf = new char[length+1]; + std::string out; +#ifdef WINDOWS + int fd = -1; +#else + int fd = open("/dev/urandom", O_RDONLY, 0); +#endif + + if (fd >= 0) + { +#ifndef WINDOWS + read(fd, randombuf, length); + close(fd); +#endif + } + else + { + for (unsigned int i = 0; i < length; i++) + randombuf[i] = rand(); + } + + for (unsigned int i = 0; i < length; i++) + { + char randchar = static_cast<char>((randombuf[i] & 0x7F) | 0x21); + out += (randchar == '=' ? '_' : randchar); + } + + delete[] randombuf; + return out; +} + +void TreeSocket::SendCapabilities() +{ + if (sentcapab) + return; + + sentcapab = true; + irc::commasepstream modulelist(MyCapabilities()); + this->WriteLine("CAPAB START"); + + /* Send module names, split at 509 length */ + std::string item = "*"; + std::string line = "CAPAB MODULES "; + while ((item = modulelist.GetToken()) != "") + { + if (line.length() + item.length() + 1 > 509) + { + this->WriteLine(line); + line = "CAPAB MODULES "; + } + + if (line != "CAPAB MODULES ") + line.append(","); + + line.append(item); + } + if (line != "CAPAB MODULES ") + this->WriteLine(line); + + int ip6 = 0; + int ip6support = 0; +#ifdef IPV6 + ip6 = 1; +#endif +#ifdef SUPPORT_IP6LINKS + ip6support = 1; +#endif + std::string extra; + /* Do we have sha256 available? If so, we send a challenge */ + if (Utils->ChallengeResponse && (Instance->FindModule("m_sha256.so"))) + { + this->SetOurChallenge(RandString(20)); + extra = " CHALLENGE=" + this->GetOurChallenge(); + } + + this->WriteLine("CAPAB CAPABILITIES :NICKMAX="+ConvToStr(NICKMAX)+" HALFOP="+ConvToStr(this->Instance->Config->AllowHalfop)+" CHANMAX="+ConvToStr(CHANMAX)+" MAXMODES="+ConvToStr(MAXMODES)+" IDENTMAX="+ConvToStr(IDENTMAX)+" MAXQUIT="+ConvToStr(MAXQUIT)+" MAXTOPIC="+ConvToStr(MAXTOPIC)+" MAXKICK="+ConvToStr(MAXKICK)+" MAXGECOS="+ConvToStr(MAXGECOS)+" MAXAWAY="+ConvToStr(MAXAWAY)+" IP6NATIVE="+ConvToStr(ip6)+" IP6SUPPORT="+ConvToStr(ip6support)+" PROTOCOL="+ConvToStr(ProtocolVersion)+extra+" PREFIX="+Instance->Modes->BuildPrefixes()+" CHANMODES="+Instance->Modes->ChanModes()); + + this->WriteLine("CAPAB END"); +} + +/* Check a comma seperated list for an item */ +bool TreeSocket::HasItem(const std::string &list, const std::string &item) +{ + irc::commasepstream seplist(list); + std::string item2 = "*"; + while ((item2 = seplist.GetToken()) != "") + { + if (item2 == item) + return true; + } + return false; +} + +/* Isolate and return the elements that are different between two comma seperated lists */ +std::string TreeSocket::ListDifference(const std::string &one, const std::string &two) +{ + irc::commasepstream list_one(one); + std::string item = "*"; + std::string result; + while ((item = list_one.GetToken()) != "") + { + if (!HasItem(two, item)) + { + result.append(" "); + result.append(item); + } + } + return result; +} + +void TreeSocket::SendError(const std::string &errormessage) +{ + /* Display the error locally as well as sending it remotely */ + this->WriteLine("ERROR :"+errormessage); + this->Instance->SNO->WriteToSnoMask('l',"Sent \2ERROR\2 to "+this->InboundServerName+": "+errormessage); + /* One last attempt to make sure the error reaches its target */ + this->FlushWriteBuffer(); +} + +bool TreeSocket::Capab(const std::deque<std::string> ¶ms) +{ + if (params.size() < 1) + { + this->SendError("Invalid number of parameters for CAPAB - Mismatched version"); + return false; + } + if (params[0] == "START") + { + this->ModuleList.clear(); + this->CapKeys.clear(); + } + else if (params[0] == "END") + { + std::string reason; + int ip6support = 0; +#ifdef SUPPORT_IP6LINKS + ip6support = 1; +#endif + /* Compare ModuleList and check CapKeys... + * Maybe this could be tidier? -- Brain + */ + if ((this->ModuleList != this->MyCapabilities()) && (this->ModuleList.length())) + { + std::string diff = ListDifference(this->ModuleList, this->MyCapabilities()); + if (!diff.length()) + { + diff = "your server:" + ListDifference(this->MyCapabilities(), this->ModuleList); + } + else + { + diff = "this server:" + diff; + } + if (diff.length() == 12) + reason = "Module list in CAPAB is not alphabetically ordered, cannot compare lists."; + else + reason = "Modules loaded on these servers are not correctly matched, these modules are not loaded on " + diff; + } + + cap_validation valid_capab[] = { + {"Maximum nickname lengths differ or remote nickname length not specified", "NICKMAX", NICKMAX}, + {"Maximum ident lengths differ or remote ident length not specified", "IDENTMAX", IDENTMAX}, + {"Maximum channel lengths differ or remote channel length not specified", "CHANMAX", CHANMAX}, + {"Maximum modes per line differ or remote modes per line not specified", "MAXMODES", MAXMODES}, + {"Maximum quit lengths differ or remote quit length not specified", "MAXQUIT", MAXQUIT}, + {"Maximum topic lengths differ or remote topic length not specified", "MAXTOPIC", MAXTOPIC}, + {"Maximum kick lengths differ or remote kick length not specified", "MAXKICK", MAXKICK}, + {"Maximum GECOS (fullname) lengths differ or remote GECOS length not specified", "MAXGECOS", MAXGECOS}, + {"Maximum awaymessage lengths differ or remote awaymessage length not specified", "MAXAWAY", MAXAWAY}, + {"", "", 0} + }; + + if (((this->CapKeys.find("IP6SUPPORT") == this->CapKeys.end()) && (ip6support)) || ((this->CapKeys.find("IP6SUPPORT") != this->CapKeys.end()) && (this->CapKeys.find("IP6SUPPORT")->second != ConvToStr(ip6support)))) + reason = "We don't both support linking to IPV6 servers"; + if (((this->CapKeys.find("IP6NATIVE") != this->CapKeys.end()) && (this->CapKeys.find("IP6NATIVE")->second == "1")) && (!ip6support)) + reason = "The remote server is IPV6 native, and we don't support linking to IPV6 servers"; + if (((this->CapKeys.find("PROTOCOL") == this->CapKeys.end()) || ((this->CapKeys.find("PROTOCOL") != this->CapKeys.end()) && (this->CapKeys.find("PROTOCOL")->second != ConvToStr(ProtocolVersion))))) + { + if (this->CapKeys.find("PROTOCOL") != this->CapKeys.end()) + reason = "Mismatched protocol versions "+this->CapKeys.find("PROTOCOL")->second+" and "+ConvToStr(ProtocolVersion); + else + reason = "Protocol version not specified"; + } + + if(this->CapKeys.find("PREFIX") != this->CapKeys.end() && this->CapKeys.find("PREFIX")->second != this->Instance->Modes->BuildPrefixes()) + reason = "One or more of the prefixes on the remote server are invalid on this server."; + + if (((this->CapKeys.find("HALFOP") == this->CapKeys.end()) && (Instance->Config->AllowHalfop)) || ((this->CapKeys.find("HALFOP") != this->CapKeys.end()) && (this->CapKeys.find("HALFOP")->second != ConvToStr(Instance->Config->AllowHalfop)))) + reason = "We don't both have halfop support enabled/disabled identically"; + + for (int x = 0; valid_capab[x].size; ++x) + { + if (((this->CapKeys.find(valid_capab[x].key) == this->CapKeys.end()) || ((this->CapKeys.find(valid_capab[x].key) != this->CapKeys.end()) && + (this->CapKeys.find(valid_capab[x].key)->second != ConvToStr(valid_capab[x].size))))) + reason = valid_capab[x].reason; + } + + /* Challenge response, store their challenge for our password */ + std::map<std::string,std::string>::iterator n = this->CapKeys.find("CHALLENGE"); + if (Utils->ChallengeResponse && (n != this->CapKeys.end()) && (Instance->FindModule("m_sha256.so"))) + { + /* Challenge-response is on now */ + this->SetTheirChallenge(n->second); + if (!this->GetTheirChallenge().empty() && (this->LinkState == CONNECTING)) + { + this->WriteLine(std::string("SERVER ")+this->Instance->Config->ServerName+" "+this->MakePass(OutboundPass, this->GetTheirChallenge())+" 0 :"+this->Instance->Config->ServerDesc); + } + } + else + { + /* They didnt specify a challenge or we don't have m_sha256.so, we use plaintext */ + if (this->LinkState == CONNECTING) + this->WriteLine(std::string("SERVER ")+this->Instance->Config->ServerName+" "+OutboundPass+" 0 :"+this->Instance->Config->ServerDesc); + } + + if (reason.length()) + { + this->SendError("CAPAB negotiation failed: "+reason); + return false; + } + } + else if ((params[0] == "MODULES") && (params.size() == 2)) + { + if (!this->ModuleList.length()) + { + this->ModuleList.append(params[1]); + } + else + { + this->ModuleList.append(","); + this->ModuleList.append(params[1]); + } + } + + else if ((params[0] == "CAPABILITIES") && (params.size() == 2)) + { + irc::tokenstream capabs(params[1]); + std::string item; + bool more = true; + while ((more = capabs.GetToken(item))) + { + /* Process each key/value pair */ + std::string::size_type equals = item.rfind('='); + if (equals != std::string::npos) + { + std::string var = item.substr(0, equals); + std::string value = item.substr(equals+1, item.length()); + CapKeys[var] = value; + } + } + } + return true; +} + +/** This function forces this server to quit, removing this server + * and any users on it (and servers and users below that, etc etc). + * It's very slow and pretty clunky, but luckily unless your network + * is having a REAL bad hair day, this function shouldnt be called + * too many times a month ;-) + */ +void TreeSocket::SquitServer(std::string &from, TreeServer* Current) +{ + /* recursively squit the servers attached to 'Current'. + * We're going backwards so we don't remove users + * while we still need them ;) + */ + for (unsigned int q = 0; q < Current->ChildCount(); q++) + { + TreeServer* recursive_server = Current->GetChild(q); + this->SquitServer(from,recursive_server); + } + /* Now we've whacked the kids, whack self */ + num_lost_servers++; + num_lost_users += Current->QuitUsers(from); +} + +/** This is a wrapper function for SquitServer above, which + * does some validation first and passes on the SQUIT to all + * other remaining servers. + */ +void TreeSocket::Squit(TreeServer* Current, const std::string &reason) +{ + if ((Current) && (Current != Utils->TreeRoot)) + { + Event rmode((char*)Current->GetName().c_str(), (Module*)Utils->Creator, "lost_server"); + rmode.Send(Instance); + + std::deque<std::string> params; + params.push_back(Current->GetName()); + params.push_back(":"+reason); + Utils->DoOneToAllButSender(Current->GetParent()->GetName(),"SQUIT",params,Current->GetName()); + if (Current->GetParent() == Utils->TreeRoot) + { + this->Instance->SNO->WriteToSnoMask('l',"Server \002"+Current->GetName()+"\002 split: "+reason); + } + else + { + this->Instance->SNO->WriteToSnoMask('l',"Server \002"+Current->GetName()+"\002 split from server \002"+Current->GetParent()->GetName()+"\002 with reason: "+reason); + } + num_lost_servers = 0; + num_lost_users = 0; + std::string from = Current->GetParent()->GetName()+" "+Current->GetName(); + SquitServer(from, Current); + Current->Tidy(); + Current->GetParent()->DelChild(Current); + DELETE(Current); + this->Instance->SNO->WriteToSnoMask('l',"Netsplit complete, lost \002%d\002 users on \002%d\002 servers.", num_lost_users, num_lost_servers); + } + else + Instance->Log(DEFAULT,"Squit from unknown server"); +} + +/** FMODE command - server mode with timestamp checks */ +bool TreeSocket::ForceMode(const std::string &source, std::deque<std::string> ¶ms) +{ + /* Chances are this is a 1.0 FMODE without TS */ + if (params.size() < 3) + { + /* No modes were in the command, probably a channel with no modes set on it */ + return true; + } + + bool smode = false; + std::string sourceserv; + /* Are we dealing with an FMODE from a user, or from a server? */ + userrec* who = this->Instance->FindNick(source); + if (who) + { + /* FMODE from a user, set sourceserv to the users server name */ + sourceserv = who->server; + } + else + { + /* FMODE from a server, create a fake user to receive mode feedback */ + who = new userrec(this->Instance); + who->SetFd(FD_MAGIC_NUMBER); + smode = true; /* Setting this flag tells us we should free the userrec later */ + sourceserv = source; /* Set sourceserv to the actual source string */ + } + const char* modelist[64]; + time_t TS = 0; + int n = 0; + memset(&modelist,0,sizeof(modelist)); + for (unsigned int q = 0; (q < params.size()) && (q < 64); q++) + { + if (q == 1) + { + /* The timestamp is in this position. + * We don't want to pass that up to the + * server->client protocol! + */ + TS = atoi(params[q].c_str()); + } + else + { + /* Everything else is fine to append to the modelist */ + modelist[n++] = params[q].c_str(); + } + + } + /* Extract the TS value of the object, either userrec or chanrec */ + userrec* dst = this->Instance->FindNick(params[0]); + chanrec* chan = NULL; + time_t ourTS = 0; + if (dst) + { + ourTS = dst->age; + } + else + { + chan = this->Instance->FindChan(params[0]); + if (chan) + { + ourTS = chan->age; + } + else + /* Oops, channel doesnt exist! */ + return true; + } + + if (!TS) + { + Instance->Log(DEFAULT,"*** BUG? *** TS of 0 sent to FMODE. Are some services authors smoking craq, or is it 1970 again?. Dropped."); + Instance->SNO->WriteToSnoMask('d', "WARNING: The server %s is sending FMODE with a TS of zero. Total craq. Mode was dropped.", sourceserv.c_str()); + return true; + } + + /* TS is equal or less: Merge the mode changes into ours and pass on. + */ + if (TS <= ourTS) + { + if ((TS < ourTS) && (!dst)) + Instance->Log(DEFAULT,"*** BUG *** Channel TS sent in FMODE to %s is %lu which is not equal to %lu!", params[0].c_str(), TS, ourTS); + + if (smode) + { + this->Instance->SendMode(modelist, n, who); + } + else + { + this->Instance->CallCommandHandler("MODE", modelist, n, who); + } + /* HOT POTATO! PASS IT ON! */ + Utils->DoOneToAllButSender(source,"FMODE",params,sourceserv); + } + /* If the TS is greater than ours, we drop the mode and dont pass it anywhere. + */ + + if (smode) + DELETE(who); + + return true; +} + +/** FTOPIC command */ +bool TreeSocket::ForceTopic(const std::string &source, std::deque<std::string> ¶ms) +{ + if (params.size() != 4) + return true; + time_t ts = atoi(params[1].c_str()); + std::string nsource = source; + chanrec* c = this->Instance->FindChan(params[0]); + if (c) + { + if ((ts >= c->topicset) || (!*c->topic)) + { + std::string oldtopic = c->topic; + strlcpy(c->topic,params[3].c_str(),MAXTOPIC); + strlcpy(c->setby,params[2].c_str(),127); + c->topicset = ts; + /* if the topic text is the same as the current topic, + * dont bother to send the TOPIC command out, just silently + * update the set time and set nick. + */ + if (oldtopic != params[3]) + { + userrec* user = this->Instance->FindNick(source); + if (!user) + { + c->WriteChannelWithServ(Instance->Config->ServerName, "TOPIC %s :%s", c->name, c->topic); + } + else + { + c->WriteChannel(user, "TOPIC %s :%s", c->name, c->topic); + nsource = user->server; + } + /* all done, send it on its way */ + params[3] = ":" + params[3]; + Utils->DoOneToAllButSender(source,"FTOPIC",params,nsource); + } + } + + } + return true; +} + +/** FJOIN, similar to TS6 SJOIN, but not quite. */ +bool TreeSocket::ForceJoin(const std::string &source, std::deque<std::string> ¶ms) +{ + /* 1.1 FJOIN works as follows: + * + * Each FJOIN is sent along with a timestamp, and the side with the lowest + * timestamp 'wins'. From this point on we will refer to this side as the + * winner. The side with the higher timestamp loses, from this point on we + * will call this side the loser or losing side. This should be familiar to + * anyone who's dealt with dreamforge or TS6 before. + * + * When two sides of a split heal and this occurs, the following things + * will happen: + * + * If the timestamps are exactly equal, both sides merge their privilages + * and users, as in InspIRCd 1.0 and ircd2.8. The channels have not been + * re-created during a split, this is safe to do. + * + * If the timestamps are NOT equal, the losing side removes all of its + * modes from the channel, before introducing new users into the channel + * which are listed in the FJOIN command's parameters. The losing side then + * LOWERS its timestamp value of the channel to match that of the winning + * side, and the modes of the users of the winning side are merged in with + * the losing side. + * + * The winning side on the other hand will ignore all user modes from the + * losing side, so only its own modes get applied. Life is simple for those + * who succeed at internets. :-) + * + * NOTE: Unlike TS6 and dreamforge and other protocols which have SJOIN, + * FJOIN does not contain the simple-modes such as +iklmnsp. Why not, + * you ask? Well, quite simply because we don't need to. They'll be sent + * after the FJOIN by FMODE, and FMODE is timestamped, so in the event + * the losing side sends any modes for the channel which shouldnt win, + * they wont as their timestamp will be too high :-) + */ + + if (params.size() < 3) + return true; + + irc::modestacker modestack(true); /* Modes to apply from the users in the user list */ + userrec* who = NULL; /* User we are currently checking */ + std::string channel = params[0]; /* Channel name, as a string */ + time_t TS = atoi(params[1].c_str()); /* Timestamp given to us for remote side */ + irc::tokenstream users(params[2]); /* Users from the user list */ + bool apply_other_sides_modes = true; /* True if we are accepting the other side's modes */ + chanrec* chan = this->Instance->FindChan(channel); /* The channel we're sending joins to */ + time_t ourTS = chan ? chan->age : Instance->Time(true)+600; /* The TS of our side of the link */ + bool created = !chan; /* True if the channel doesnt exist here yet */ + std::string item; /* One item in the list of nicks */ + + params[2] = ":" + params[2]; + Utils->DoOneToAllButSender(source,"FJOIN",params,source); + + if (!TS) + { + Instance->Log(DEFAULT,"*** BUG? *** TS of 0 sent to FJOIN. Are some services authors smoking craq, or is it 1970 again?. Dropped."); + Instance->SNO->WriteToSnoMask('d', "WARNING: The server %s is sending FJOIN with a TS of zero. Total craq. Command was dropped.", source.c_str()); + return true; + } + + /* If our TS is less than theirs, we dont accept their modes */ + if (ourTS < TS) + apply_other_sides_modes = false; + + /* Our TS greater than theirs, clear all our modes from the channel, accept theirs. */ + if (ourTS > TS) + { + std::deque<std::string> param_list; + if (Utils->AnnounceTSChange && chan) + chan->WriteChannelWithServ(Instance->Config->ServerName, "NOTICE %s :TS for %s changed from %lu to %lu", chan->name, chan->name, ourTS, TS); + ourTS = TS; + if (!created) + { + chan->age = TS; + param_list.push_back(channel); + this->RemoveStatus(Instance->Config->ServerName, param_list); + } + } + + /* Now, process every 'prefixes,nick' pair */ + while (users.GetToken(item)) + { + const char* usr = item.c_str(); + if (usr && *usr) + { + const char* permissions = usr; + /* Iterate through all the prefix values, convert them from prefixes to mode letters */ + std::string modes; + while ((*permissions) && (*permissions != ',')) + { + ModeHandler* mh = Instance->Modes->FindPrefix(*permissions); + if (mh) + modes = modes + mh->GetModeChar(); + else + { + this->SendError(std::string("Invalid prefix '")+(*permissions)+"' in FJOIN"); + return false; + } + usr++; + permissions++; + } + /* Advance past the comma, to the nick */ + usr++; + + /* Check the user actually exists */ + who = this->Instance->FindNick(usr); + if (who) + { + /* Check that the user's 'direction' is correct */ + TreeServer* route_back_again = Utils->BestRouteTo(who->server); + if ((!route_back_again) || (route_back_again->GetSocket() != this)) + continue; + + /* Add any permissions this user had to the mode stack */ + for (std::string::iterator x = modes.begin(); x != modes.end(); ++x) + modestack.Push(*x, who->nick); + + chanrec::JoinUser(this->Instance, who, channel.c_str(), true, "", TS); + } + else + { + Instance->Log(SPARSE,"Warning! Invalid user %s in FJOIN to channel %s IGNORED", usr, channel.c_str()); + continue; + } + } + } + + /* Flush mode stacker if we lost the FJOIN or had equal TS */ + if (apply_other_sides_modes) + { + std::deque<std::string> stackresult; + const char* mode_junk[MAXMODES+2]; + userrec* n = new userrec(Instance); + n->SetFd(FD_MAGIC_NUMBER); + mode_junk[0] = channel.c_str(); + + while (modestack.GetStackedLine(stackresult)) + { + for (size_t j = 0; j < stackresult.size(); j++) + { + mode_junk[j+1] = stackresult[j].c_str(); + } + Instance->SendMode(mode_junk, stackresult.size() + 1, n); + } + + delete n; + } + + return true; +} + +/** NICK command */ +bool TreeSocket::IntroduceClient(const std::string &source, std::deque<std::string> ¶ms) +{ + /** Do we have enough parameters: + * NICK age nick host dhost ident +modes ip :gecos + */ + if (params.size() != 8) + { + this->WriteLine(std::string(":")+this->Instance->Config->ServerName+" KILL "+params[1]+" :Invalid client introduction ("+params[1]+"?)"); + return true; + } + + time_t age = ConvToInt(params[0]); + const char* tempnick = params[1].c_str(); + + cmd_validation valid[] = { {"Nickname", 1, NICKMAX}, {"Hostname", 2, 64}, {"Displayed hostname", 3, 64}, {"Ident", 4, IDENTMAX}, {"GECOS", 7, MAXGECOS}, {"", 0, 0} }; + + TreeServer* remoteserver = Utils->FindServer(source); + if (!remoteserver) + { + this->WriteLine(std::string(":")+this->Instance->Config->ServerName+" KILL "+params[1]+" :Invalid client introduction (Unknown server "+source+")"); + return true; + } + + /* Check parameters for validity before introducing the client, discovered by dmb */ + if (!age) + { + this->WriteLine(std::string(":")+this->Instance->Config->ServerName+" KILL "+params[1]+" :Invalid client introduction (Invalid TS?)"); + return true; + } + for (size_t x = 0; valid[x].length; ++x) + { + if (params[valid[x].param].length() > valid[x].length) + { + this->WriteLine(std::string(":")+this->Instance->Config->ServerName+" KILL "+params[1]+" :Invalid client introduction (" + valid[x].item + " > " + ConvToStr(valid[x].length) + ")"); + return true; + } + } + + /** Our client looks ok, lets introduce it now + */ + Instance->Log(DEBUG,"New remote client %s",tempnick); + user_hash::iterator iter = this->Instance->clientlist->find(tempnick); + + if (iter != this->Instance->clientlist->end()) + { + /* nick collision */ + this->WriteLine(std::string(":")+this->Instance->Config->ServerName+" KILL "+tempnick+" :Nickname collision"); + userrec::QuitUser(this->Instance, iter->second, "Nickname collision"); + return true; + } + + userrec* _new = new userrec(this->Instance); + (*(this->Instance->clientlist))[tempnick] = _new; + _new->SetFd(FD_MAGIC_NUMBER); + strlcpy(_new->nick, tempnick,NICKMAX-1); + strlcpy(_new->host, params[2].c_str(),64); + strlcpy(_new->dhost, params[3].c_str(),64); + _new->server = this->Instance->FindServerNamePtr(source.c_str()); + strlcpy(_new->ident, params[4].c_str(),IDENTMAX); + strlcpy(_new->fullname, params[7].c_str(),MAXGECOS); + _new->registered = REG_ALL; + _new->signon = age; + + /* we need to remove the + from the modestring, so we can do our stuff */ + std::string::size_type pos_after_plus = params[5].find_first_not_of('+'); + if (pos_after_plus != std::string::npos) + params[5] = params[5].substr(pos_after_plus); + + for (std::string::iterator v = params[5].begin(); v != params[5].end(); v++) + { + _new->modes[(*v)-65] = 1; + /* For each mode thats set, increase counter */ + ModeHandler* mh = Instance->Modes->FindMode(*v, MODETYPE_USER); + if (mh) + mh->ChangeCount(1); + } + + /* now we've done with modes processing, put the + back for remote servers */ + params[5] = "+" + params[5]; + +#ifdef SUPPORT_IP6LINKS + if (params[6].find_first_of(":") != std::string::npos) + _new->SetSockAddr(AF_INET6, params[6].c_str(), 0); + else +#endif + _new->SetSockAddr(AF_INET, params[6].c_str(), 0); + + Instance->AddGlobalClone(_new); + + bool dosend = !(((this->Utils->quiet_bursts) && (this->bursting || Utils->FindRemoteBurstServer(remoteserver))) || (this->Instance->SilentULine(_new->server))); + + if (dosend) + this->Instance->SNO->WriteToSnoMask('C',"Client connecting at %s: %s!%s@%s [%s] [%s]",_new->server,_new->nick,_new->ident,_new->host, _new->GetIPString(), _new->fullname); + + params[7] = ":" + params[7]; + Utils->DoOneToAllButSender(source,"NICK", params, source); + + // Increment the Source Servers User Count.. + TreeServer* SourceServer = Utils->FindServer(source); + if (SourceServer) + { + SourceServer->AddUserCount(); + } + + FOREACH_MOD_I(Instance,I_OnPostConnect,OnPostConnect(_new)); + + return true; +} + +/** 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. + */ +void TreeSocket::SendFJoins(TreeServer* Current, chanrec* c) +{ + std::string buffer; + char list[MAXBUF]; + std::string individual_halfops = std::string(":")+this->Instance->Config->ServerName+" FMODE "+c->name+" "+ConvToStr(c->age); + + size_t dlen, curlen; + dlen = curlen = snprintf(list,MAXBUF,":%s FJOIN %s %lu",this->Instance->Config->ServerName,c->name,(unsigned long)c->age); + int numusers = 0; + char* ptr = list + dlen; + + CUList *ulist = c->GetUsers(); + std::string modes; + std::string params; + + for (CUList::iterator i = ulist->begin(); i != ulist->end(); i++) + { + // The first parameter gets a : before it + size_t ptrlen = snprintf(ptr, MAXBUF, " %s%s,%s", !numusers ? ":" : "", c->GetAllPrefixChars(i->first), i->first->nick); + + curlen += ptrlen; + ptr += ptrlen; + + numusers++; + + if (curlen > (480-NICKMAX)) + { + buffer.append(list).append("\r\n"); + dlen = curlen = snprintf(list,MAXBUF,":%s FJOIN %s %lu",this->Instance->Config->ServerName,c->name,(unsigned long)c->age); + ptr = list + dlen; + ptrlen = 0; + numusers = 0; + } + } + + if (numusers) + buffer.append(list).append("\r\n"); + + buffer.append(":").append(this->Instance->Config->ServerName).append(" FMODE ").append(c->name).append(" ").append(ConvToStr(c->age)).append(" +").append(c->ChanModes(true)).append("\r\n"); + + int linesize = 1; + for (BanList::iterator b = c->bans.begin(); b != c->bans.end(); b++) + { + int size = strlen(b->data) + 2; + int currsize = linesize + size; + if (currsize <= 350) + { + modes.append("b"); + params.append(" ").append(b->data); + linesize += size; + } + if ((params.length() >= MAXMODES) || (currsize > 350)) + { + /* Wrap at MAXMODES */ + buffer.append(":").append(this->Instance->Config->ServerName).append(" FMODE ").append(c->name).append(" ").append(ConvToStr(c->age)).append(" +").append(modes).append(params).append("\r\n"); + modes.clear(); + params.clear(); + linesize = 1; + } + } + + /* Only send these if there are any */ + if (!modes.empty()) + buffer.append(":").append(this->Instance->Config->ServerName).append(" FMODE ").append(c->name).append(" ").append(ConvToStr(c->age)).append(" +").append(modes).append(params); + + this->WriteLine(buffer); +} + +/** Send G, Q, Z and E lines */ +void TreeSocket::SendXLines(TreeServer* Current) +{ + char data[MAXBUF]; + std::string buffer; + std::string n = this->Instance->Config->ServerName; + const char* sn = n.c_str(); + /* Yes, these arent too nice looking, but they get the job done */ + for (std::vector<ZLine*>::iterator i = Instance->XLines->zlines.begin(); i != Instance->XLines->zlines.end(); i++) + { + snprintf(data,MAXBUF,":%s ADDLINE Z %s %s %lu %lu :%s\r\n",sn,(*i)->ipaddr,(*i)->source,(unsigned long)(*i)->set_time,(unsigned long)(*i)->duration,(*i)->reason); + buffer.append(data); + } + for (std::vector<QLine*>::iterator i = Instance->XLines->qlines.begin(); i != Instance->XLines->qlines.end(); i++) + { + snprintf(data,MAXBUF,":%s ADDLINE Q %s %s %lu %lu :%s\r\n",sn,(*i)->nick,(*i)->source,(unsigned long)(*i)->set_time,(unsigned long)(*i)->duration,(*i)->reason); + buffer.append(data); + } + for (std::vector<GLine*>::iterator i = Instance->XLines->glines.begin(); i != Instance->XLines->glines.end(); i++) + { + snprintf(data,MAXBUF,":%s ADDLINE G %s@%s %s %lu %lu :%s\r\n",sn,(*i)->identmask,(*i)->hostmask,(*i)->source,(unsigned long)(*i)->set_time,(unsigned long)(*i)->duration,(*i)->reason); + buffer.append(data); + } + for (std::vector<ELine*>::iterator i = Instance->XLines->elines.begin(); i != Instance->XLines->elines.end(); i++) + { + snprintf(data,MAXBUF,":%s ADDLINE E %s@%s %s %lu %lu :%s\r\n",sn,(*i)->identmask,(*i)->hostmask,(*i)->source,(unsigned long)(*i)->set_time,(unsigned long)(*i)->duration,(*i)->reason); + buffer.append(data); + } + for (std::vector<ZLine*>::iterator i = Instance->XLines->pzlines.begin(); i != Instance->XLines->pzlines.end(); i++) + { + snprintf(data,MAXBUF,":%s ADDLINE Z %s %s %lu %lu :%s\r\n",sn,(*i)->ipaddr,(*i)->source,(unsigned long)(*i)->set_time,(unsigned long)(*i)->duration,(*i)->reason); + buffer.append(data); + } + for (std::vector<QLine*>::iterator i = Instance->XLines->pqlines.begin(); i != Instance->XLines->pqlines.end(); i++) + { + snprintf(data,MAXBUF,":%s ADDLINE Q %s %s %lu %lu :%s\r\n",sn,(*i)->nick,(*i)->source,(unsigned long)(*i)->set_time,(unsigned long)(*i)->duration,(*i)->reason); + buffer.append(data); + } + for (std::vector<GLine*>::iterator i = Instance->XLines->pglines.begin(); i != Instance->XLines->pglines.end(); i++) + { + snprintf(data,MAXBUF,":%s ADDLINE G %s@%s %s %lu %lu :%s\r\n",sn,(*i)->identmask,(*i)->hostmask,(*i)->source,(unsigned long)(*i)->set_time,(unsigned long)(*i)->duration,(*i)->reason); + buffer.append(data); + } + for (std::vector<ELine*>::iterator i = Instance->XLines->pelines.begin(); i != Instance->XLines->pelines.end(); i++) + { + snprintf(data,MAXBUF,":%s ADDLINE E %s@%s %s %lu %lu :%s\r\n",sn,(*i)->identmask,(*i)->hostmask,(*i)->source,(unsigned long)(*i)->set_time,(unsigned long)(*i)->duration,(*i)->reason); + buffer.append(data); + } + + if (!buffer.empty()) + this->WriteLine(buffer); +} + +/** Send channel modes and topics */ +void TreeSocket::SendChannelModes(TreeServer* Current) +{ + char data[MAXBUF]; + std::deque<std::string> list; + std::string n = this->Instance->Config->ServerName; + const char* sn = n.c_str(); + Instance->Log(DEBUG,"Sending channels and modes, %d to send", this->Instance->chanlist->size()); + for (chan_hash::iterator c = this->Instance->chanlist->begin(); c != this->Instance->chanlist->end(); c++) + { + SendFJoins(Current, c->second); + if (*c->second->topic) + { + snprintf(data,MAXBUF,":%s FTOPIC %s %lu %s :%s",sn,c->second->name,(unsigned long)c->second->topicset,c->second->setby,c->second->topic); + this->WriteLine(data); + } + FOREACH_MOD_I(this->Instance,I_OnSyncChannel,OnSyncChannel(c->second,(Module*)Utils->Creator,(void*)this)); + list.clear(); + c->second->GetExtList(list); + for (unsigned int j = 0; j < list.size(); j++) + { + FOREACH_MOD_I(this->Instance,I_OnSyncChannelMetaData,OnSyncChannelMetaData(c->second,(Module*)Utils->Creator,(void*)this,list[j])); + } + } +} + +/** send all users and their oper state/modes */ +void TreeSocket::SendUsers(TreeServer* Current) +{ + char data[MAXBUF]; + std::deque<std::string> list; + std::string dataline; + for (user_hash::iterator u = this->Instance->clientlist->begin(); u != this->Instance->clientlist->end(); u++) + { + if (u->second->registered == REG_ALL) + { + snprintf(data,MAXBUF,":%s NICK %lu %s %s %s %s +%s %s :%s",u->second->server,(unsigned long)u->second->age,u->second->nick,u->second->host,u->second->dhost,u->second->ident,u->second->FormatModes(),u->second->GetIPString(),u->second->fullname); + this->WriteLine(data); + if (*u->second->oper) + { + snprintf(data,MAXBUF,":%s OPERTYPE %s", u->second->nick, u->second->oper); + this->WriteLine(data); + } + if (*u->second->awaymsg) + { + snprintf(data,MAXBUF,":%s AWAY :%s", u->second->nick, u->second->awaymsg); + this->WriteLine(data); + } + } + } + for (user_hash::iterator u = this->Instance->clientlist->begin(); u != this->Instance->clientlist->end(); u++) + { + FOREACH_MOD_I(this->Instance,I_OnSyncUser,OnSyncUser(u->second,(Module*)Utils->Creator,(void*)this)); + list.clear(); + u->second->GetExtList(list); + for (unsigned int j = 0; j < list.size(); j++) + { + FOREACH_MOD_I(this->Instance,I_OnSyncUserMetaData,OnSyncUserMetaData(u->second,(Module*)Utils->Creator,(void*)this,list[j])); + } + } +} + +/** 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 + * users require their servers to exist, and channels require their + * users to exist. You get the idea. + */ +void TreeSocket::DoBurst(TreeServer* s) +{ + std::string name = s->GetName(); + std::string burst = "BURST "+ConvToStr(Instance->Time(true)); + std::string endburst = "ENDBURST"; + this->Instance->SNO->WriteToSnoMask('l',"Bursting to \2%s\2 (Authentication: %s).", name.c_str(), this->GetTheirChallenge().empty() ? "plaintext password" : "SHA256-HMAC challenge-response"); + this->WriteLine(burst); + /* send our version string */ + this->WriteLine(std::string(":")+this->Instance->Config->ServerName+" VERSION :"+this->Instance->GetVersionString()); + /* Send server tree */ + this->SendServers(Utils->TreeRoot,s,1); + /* Send users and their oper status */ + this->SendUsers(s); + /* Send everything else (channel modes, xlines etc) */ + this->SendChannelModes(s); + this->SendXLines(s); + FOREACH_MOD_I(this->Instance,I_OnSyncOtherMetaData,OnSyncOtherMetaData((Module*)Utils->Creator,(void*)this)); + this->WriteLine(endburst); + this->Instance->SNO->WriteToSnoMask('l',"Finished bursting to \2"+name+"\2."); +} + +/** This function is called when we receive data from a remote + * server. We buffer the data in a std::string (it doesnt stay + * there for long), reading using InspSocket::Read() which can + * read up to 16 kilobytes in one operation. + * + * IF THIS FUNCTION RETURNS FALSE, THE CORE CLOSES AND DELETES + * THE SOCKET OBJECT FOR US. + */ +bool TreeSocket::OnDataReady() +{ + char* data = this->Read(); + /* Check that the data read is a valid pointer and it has some content */ + if (data && *data) + { + this->in_buffer.append(data); + /* While there is at least one new line in the buffer, + * do something useful (we hope!) with it. + */ + while (in_buffer.find("\n") != std::string::npos) + { + std::string ret = in_buffer.substr(0,in_buffer.find("\n")-1); + in_buffer = in_buffer.substr(in_buffer.find("\n")+1,in_buffer.length()-in_buffer.find("\n")); + /* Use rfind here not find, as theres more + * chance of the \r being near the end of the + * string, not the start. + */ + if (ret.find("\r") != std::string::npos) + ret = in_buffer.substr(0,in_buffer.find("\r")-1); + /* Process this one, abort if it + * didnt return true. + */ + if (!this->ProcessLine(ret)) + { + return false; + } + } + return true; + } + /* EAGAIN returns an empty but non-NULL string, so this + * evaluates to TRUE for EAGAIN but to FALSE for EOF. + */ + return (data && !*data); +} + diff --git a/src/modules/m_spanningtree/treesocket2.cpp b/src/modules/m_spanningtree/treesocket2.cpp index d383e2394..f518151e9 100644 --- a/src/modules/m_spanningtree/treesocket2.cpp +++ b/src/modules/m_spanningtree/treesocket2.cpp @@ -1 +1,1554 @@ -/* +------------------------------------+
* | 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 "users.h"
#include "channels.h"
#include "modules.h"
#include "commands/cmd_whois.h"
#include "commands/cmd_stats.h"
#include "socket.h"
#include "wildcard.h"
#include "xline.h"
#include "transport.h"
#include "socketengine.h"
#include "m_spanningtree/main.h"
#include "m_spanningtree/utils.h"
#include "m_spanningtree/treeserver.h"
#include "m_spanningtree/link.h"
#include "m_spanningtree/treesocket.h"
#include "m_spanningtree/resolvers.h"
#include "m_spanningtree/handshaketimer.h"
/* $ModDep: m_spanningtree/timesynctimer.h m_spanningtree/resolvers.h m_spanningtree/main.h m_spanningtree/utils.h m_spanningtree/treeserver.h m_spanningtree/link.h m_spanningtree/treesocket.h */
static std::map<std::string, std::string> warned; /* Server names that have had protocol violation warnings displayed for them */
int TreeSocket::WriteLine(std::string line)
{
Instance->Log(DEBUG, "S[%d] -> %s", this->GetFd(), line.c_str());
line.append("\r\n");
return this->Write(line);
}
/* Handle ERROR command */
bool TreeSocket::Error(std::deque<std::string> ¶ms)
{
if (params.size() < 1)
return false;
this->Instance->SNO->WriteToSnoMask('l',"ERROR from %s: %s",(!InboundServerName.empty() ? InboundServerName.c_str() : myhost.c_str()),params[0].c_str());
/* we will return false to cause the socket to close. */
return false;
}
bool TreeSocket::Modules(const std::string &prefix, std::deque<std::string> ¶ms)
{
if (params.empty())
return true;
if (!this->Instance->MatchText(this->Instance->Config->ServerName, params[0]))
{
/* Pass it on, not for us */
Utils->DoOneToOne(prefix, "MODULES", params, params[0]);
return true;
}
char strbuf[MAXBUF];
std::deque<std::string> par;
par.push_back(prefix);
par.push_back("");
userrec* source = this->Instance->FindNick(prefix);
if (!source)
return true;
for (unsigned int i = 0; i < Instance->Config->module_names.size(); i++)
{
Version V = Instance->modules[i]->GetVersion();
char modulename[MAXBUF];
char flagstate[MAXBUF];
*flagstate = 0;
if (V.Flags & VF_STATIC)
strlcat(flagstate,", static",MAXBUF);
if (V.Flags & VF_VENDOR)
strlcat(flagstate,", vendor",MAXBUF);
if (V.Flags & VF_COMMON)
strlcat(flagstate,", common",MAXBUF);
if (V.Flags & VF_SERVICEPROVIDER)
strlcat(flagstate,", service provider",MAXBUF);
if (!flagstate[0])
strcpy(flagstate," <no flags>");
strlcpy(modulename,Instance->Config->module_names[i].c_str(),256);
if (*source->oper)
{
snprintf(strbuf, MAXBUF, "::%s 900 %s :0x%08lx %d.%d.%d.%d %s (%s)",Instance->Config->ServerName,source->nick,(long unsigned int)Instance->modules[i],V.Major,V.Minor,V.Revision,V.Build,ServerConfig::CleanFilename(modulename),flagstate+2);
}
else
{
snprintf(strbuf, MAXBUF, "::%s 900 %s :%s",Instance->Config->ServerName,source->nick,ServerConfig::CleanFilename(modulename));
}
par[1] = strbuf;
Utils->DoOneToOne(Instance->Config->ServerName, "PUSH", par, source->server);
}
snprintf(strbuf, MAXBUF, "::%s 901 %s :End of MODULES list", Instance->Config->ServerName, source->nick);
par[1] = strbuf;
Utils->DoOneToOne(Instance->Config->ServerName, "PUSH", par, source->server);
return true;
}
/** remote MOTD. leet, huh? */
bool TreeSocket::Motd(const std::string &prefix, std::deque<std::string> ¶ms)
{
if (params.size() > 0)
{
if (this->Instance->MatchText(this->Instance->Config->ServerName, params[0]))
{
/* It's for our server */
string_list results;
userrec* source = this->Instance->FindNick(prefix);
if (source)
{
std::deque<std::string> par;
par.push_back(prefix);
par.push_back("");
if (!Instance->Config->MOTD.size())
{
par[1] = std::string("::")+Instance->Config->ServerName+" 422 "+source->nick+" :Message of the day file is missing.";
Utils->DoOneToOne(this->Instance->Config->ServerName, "PUSH",par, source->server);
return true;
}
par[1] = std::string("::")+Instance->Config->ServerName+" 375 "+source->nick+" :"+Instance->Config->ServerName+" message of the day";
Utils->DoOneToOne(this->Instance->Config->ServerName, "PUSH",par, source->server);
for (unsigned int i = 0; i < Instance->Config->MOTD.size(); i++)
{
par[1] = std::string("::")+Instance->Config->ServerName+" 372 "+source->nick+" :- "+Instance->Config->MOTD[i];
Utils->DoOneToOne(this->Instance->Config->ServerName, "PUSH",par, source->server);
}
par[1] = std::string("::")+Instance->Config->ServerName+" 376 "+source->nick+" End of message of the day.";
Utils->DoOneToOne(this->Instance->Config->ServerName, "PUSH",par, source->server);
}
}
else
{
/* Pass it on */
userrec* source = this->Instance->FindNick(prefix);
if (source)
Utils->DoOneToOne(prefix, "MOTD", params, params[0]);
}
}
return true;
}
/** remote ADMIN. leet, huh? */
bool TreeSocket::Admin(const std::string &prefix, std::deque<std::string> ¶ms)
{
if (params.size() > 0)
{
if (this->Instance->MatchText(this->Instance->Config->ServerName, params[0]))
{
/* It's for our server */
string_list results;
userrec* source = this->Instance->FindNick(prefix);
if (source)
{
std::deque<std::string> par;
par.push_back(prefix);
par.push_back("");
par[1] = std::string("::")+Instance->Config->ServerName+" 256 "+source->nick+" :Administrative info for "+Instance->Config->ServerName;
Utils->DoOneToOne(this->Instance->Config->ServerName, "PUSH",par, source->server);
par[1] = std::string("::")+Instance->Config->ServerName+" 257 "+source->nick+" :Name - "+Instance->Config->AdminName;
Utils->DoOneToOne(this->Instance->Config->ServerName, "PUSH",par, source->server);
par[1] = std::string("::")+Instance->Config->ServerName+" 258 "+source->nick+" :Nickname - "+Instance->Config->AdminNick;
Utils->DoOneToOne(this->Instance->Config->ServerName, "PUSH",par, source->server);
par[1] = std::string("::")+Instance->Config->ServerName+" 258 "+source->nick+" :E-Mail - "+Instance->Config->AdminEmail;
Utils->DoOneToOne(this->Instance->Config->ServerName, "PUSH",par, source->server);
}
}
else
{
/* Pass it on */
userrec* source = this->Instance->FindNick(prefix);
if (source)
Utils->DoOneToOne(prefix, "ADMIN", params, params[0]);
}
}
return true;
}
bool TreeSocket::Stats(const std::string &prefix, std::deque<std::string> ¶ms)
{
/* Get the reply to a STATS query if it matches this servername,
* and send it back as a load of PUSH queries
*/
if (params.size() > 1)
{
if (this->Instance->MatchText(this->Instance->Config->ServerName, params[1]))
{
/* It's for our server */
string_list results;
userrec* source = this->Instance->FindNick(prefix);
if (source)
{
std::deque<std::string> par;
par.push_back(prefix);
par.push_back("");
DoStats(this->Instance, *(params[0].c_str()), source, results);
for (size_t i = 0; i < results.size(); i++)
{
par[1] = "::" + results[i];
Utils->DoOneToOne(this->Instance->Config->ServerName, "PUSH",par, source->server);
}
}
}
else
{
/* Pass it on */
userrec* source = this->Instance->FindNick(prefix);
if (source)
Utils->DoOneToOne(prefix, "STATS", params, params[1]);
}
}
return true;
}
/** Because the core won't let users or even SERVERS set +o,
* we use the OPERTYPE command to do this.
*/
bool TreeSocket::OperType(const std::string &prefix, std::deque<std::string> ¶ms)
{
if (params.size() != 1)
return true;
std::string opertype = params[0];
userrec* u = this->Instance->FindNick(prefix);
if (u)
{
u->modes[UM_OPERATOR] = 1;
this->Instance->all_opers.push_back(u);
strlcpy(u->oper,opertype.c_str(),NICKMAX-1);
Utils->DoOneToAllButSender(u->nick,"OPERTYPE",params,u->server);
this->Instance->SNO->WriteToSnoMask('o',"From %s: User %s (%s@%s) is now an IRC operator of type %s",u->server, u->nick,u->ident,u->host,irc::Spacify(opertype.c_str()));
}
return true;
}
/** Because Andy insists that services-compatible servers must
* implement SVSNICK and SVSJOIN, that's exactly what we do :p
*/
bool TreeSocket::ForceNick(const std::string &prefix, std::deque<std::string> ¶ms)
{
if (params.size() < 3)
return true;
userrec* u = this->Instance->FindNick(params[0]);
if (u)
{
Utils->DoOneToAllButSender(prefix,"SVSNICK",params,prefix);
if (IS_LOCAL(u))
{
std::deque<std::string> par;
par.push_back(params[1]);
if (!u->ForceNickChange(params[1].c_str()))
{
userrec::QuitUser(this->Instance, u, "Nickname collision");
return true;
}
u->age = atoi(params[2].c_str());
}
}
return true;
}
bool TreeSocket::OperQuit(const std::string &prefix, std::deque<std::string> ¶ms)
{
if (params.size() < 1)
return true;
userrec* u = this->Instance->FindNick(prefix);
if (u)
{
u->SetOperQuit(params[0]);
params[0] = ":" + params[0];
Utils->DoOneToAllButSender(prefix,"OPERQUIT",params,prefix);
}
return true;
}
bool TreeSocket::ServiceJoin(const std::string &prefix, std::deque<std::string> ¶ms)
{
if (params.size() < 2)
return true;
userrec* u = this->Instance->FindNick(params[0]);
if (u)
{
/* only join if it's local, otherwise just pass it on! */
if (IS_LOCAL(u))
chanrec::JoinUser(this->Instance, u, params[1].c_str(), false, "", Instance->Time());
Utils->DoOneToAllButSender(prefix,"SVSJOIN",params,prefix);
}
return true;
}
bool TreeSocket::RemoteRehash(const std::string &prefix, std::deque<std::string> ¶ms)
{
if (params.size() < 1)
return false;
std::string servermask = params[0];
if (this->Instance->MatchText(this->Instance->Config->ServerName,servermask))
{
this->Instance->SNO->WriteToSnoMask('l',"Remote rehash initiated by \002"+prefix+"\002.");
this->Instance->RehashServer();
Utils->ReadConfiguration(false);
InitializeDisabledCommands(Instance->Config->DisabledCommands, Instance);
}
Utils->DoOneToAllButSender(prefix,"REHASH",params,prefix);
return true;
}
bool TreeSocket::RemoteKill(const std::string &prefix, std::deque<std::string> ¶ms)
{
if (params.size() != 2)
return true;
userrec* who = this->Instance->FindNick(params[0]);
if (who)
{
/* Prepend kill source, if we don't have one */
if (*(params[1].c_str()) != '[')
{
params[1] = "[" + prefix + "] Killed (" + params[1] +")";
}
std::string reason = params[1];
params[1] = ":" + params[1];
Utils->DoOneToAllButSender(prefix,"KILL",params,prefix);
// NOTE: This is safe with kill hiding on, as RemoteKill is only reached if we have a server prefix.
// in short this is not executed for USERS.
who->Write(":%s KILL %s :%s (%s)", prefix.c_str(), who->nick, prefix.c_str(), reason.c_str());
userrec::QuitUser(this->Instance,who,reason);
}
return true;
}
bool TreeSocket::LocalPong(const std::string &prefix, std::deque<std::string> ¶ms)
{
if (params.size() < 1)
return true;
if (params.size() == 1)
{
TreeServer* ServerSource = Utils->FindServer(prefix);
if (ServerSource)
{
ServerSource->SetPingFlag();
ServerSource->rtt = Instance->Time() - ServerSource->LastPing;
}
}
else
{
std::string forwardto = params[1];
if (forwardto == this->Instance->Config->ServerName)
{
/*
* this is a PONG for us
* if the prefix is a user, check theyre local, and if they are,
* dump the PONG reply back to their fd. If its a server, do nowt.
* Services might want to send these s->s, but we dont need to yet.
*/
userrec* u = this->Instance->FindNick(prefix);
if (u)
{
u->WriteServ("PONG %s %s",params[0].c_str(),params[1].c_str());
}
}
else
{
// not for us, pass it on :)
Utils->DoOneToOne(prefix,"PONG",params,forwardto);
}
}
return true;
}
bool TreeSocket::MetaData(const std::string &prefix, std::deque<std::string> ¶ms)
{
if (params.size() < 2)
return true;
else if (params.size() < 3)
params.push_back("");
TreeServer* ServerSource = Utils->FindServer(prefix);
if (ServerSource)
{
Utils->SetRemoteBursting(ServerSource, false);
if (params[0] == "*")
{
FOREACH_MOD_I(this->Instance,I_OnDecodeMetaData,OnDecodeMetaData(TYPE_OTHER,NULL,params[1],params[2]));
}
else if (*(params[0].c_str()) == '#')
{
chanrec* c = this->Instance->FindChan(params[0]);
if (c)
{
FOREACH_MOD_I(this->Instance,I_OnDecodeMetaData,OnDecodeMetaData(TYPE_CHANNEL,c,params[1],params[2]));
}
}
else if (*(params[0].c_str()) != '#')
{
userrec* u = this->Instance->FindNick(params[0]);
if (u)
{
FOREACH_MOD_I(this->Instance,I_OnDecodeMetaData,OnDecodeMetaData(TYPE_USER,u,params[1],params[2]));
}
}
}
params[2] = ":" + params[2];
Utils->DoOneToAllButSender(prefix,"METADATA",params,prefix);
return true;
}
bool TreeSocket::ServerVersion(const std::string &prefix, std::deque<std::string> ¶ms)
{
if (params.size() < 1)
return true;
TreeServer* ServerSource = Utils->FindServer(prefix);
if (ServerSource)
{
ServerSource->SetVersion(params[0]);
}
params[0] = ":" + params[0];
Utils->DoOneToAllButSender(prefix,"VERSION",params,prefix);
return true;
}
bool TreeSocket::ChangeHost(const std::string &prefix, std::deque<std::string> ¶ms)
{
if (params.size() < 1)
return true;
userrec* u = this->Instance->FindNick(prefix);
if (u)
{
u->ChangeDisplayedHost(params[0].c_str());
Utils->DoOneToAllButSender(prefix,"FHOST",params,u->server);
}
return true;
}
bool TreeSocket::AddLine(const std::string &prefix, std::deque<std::string> ¶ms)
{
if (params.size() < 6)
return true;
bool propogate = false;
if (!this->bursting)
Utils->lines_to_apply = 0;
switch (*(params[0].c_str()))
{
case 'Z':
propogate = Instance->XLines->add_zline(atoi(params[4].c_str()), params[2].c_str(), params[5].c_str(), params[1].c_str());
Instance->XLines->zline_set_creation_time(params[1].c_str(), atoi(params[3].c_str()));
if (propogate)
Utils->lines_to_apply |= APPLY_ZLINES;
break;
case 'Q':
propogate = Instance->XLines->add_qline(atoi(params[4].c_str()), params[2].c_str(), params[5].c_str(), params[1].c_str());
Instance->XLines->qline_set_creation_time(params[1].c_str(), atoi(params[3].c_str()));
if (propogate)
Utils->lines_to_apply |= APPLY_QLINES;
break;
case 'E':
propogate = Instance->XLines->add_eline(atoi(params[4].c_str()), params[2].c_str(), params[5].c_str(), params[1].c_str());
Instance->XLines->eline_set_creation_time(params[1].c_str(), atoi(params[3].c_str()));
break;
case 'G':
propogate = Instance->XLines->add_gline(atoi(params[4].c_str()), params[2].c_str(), params[5].c_str(), params[1].c_str());
Instance->XLines->gline_set_creation_time(params[1].c_str(), atoi(params[3].c_str()));
if (propogate)
Utils->lines_to_apply |= APPLY_GLINES;
break;
case 'K':
propogate = Instance->XLines->add_kline(atoi(params[4].c_str()), params[2].c_str(), params[5].c_str(), params[1].c_str());
if (propogate)
Utils->lines_to_apply |= APPLY_KLINES;
break;
default:
/* Just in case... */
this->Instance->SNO->WriteToSnoMask('x',"\2WARNING\2: Invalid xline type '"+params[0]+"' sent by server "+prefix+", ignored!");
propogate = false;
break;
}
/* Send it on its way */
if (propogate)
{
if (atoi(params[4].c_str()))
{
time_t c_requires_crap = ConvToInt(params[4]) + Instance->Time();
this->Instance->SNO->WriteToSnoMask('x',"%s Added %cLINE on %s to expire on %s (%s).",prefix.c_str(),*(params[0].c_str()),params[1].c_str(),Instance->TimeString(c_requires_crap).c_str(),params[5].c_str());
}
else
{
this->Instance->SNO->WriteToSnoMask('x',"%s Added permenant %cLINE on %s (%s).",prefix.c_str(),*(params[0].c_str()),params[1].c_str(),params[5].c_str());
}
params[5] = ":" + params[5];
Utils->DoOneToAllButSender(prefix,"ADDLINE",params,prefix);
}
if (!this->bursting)
{
Instance->XLines->apply_lines(Utils->lines_to_apply);
Utils->lines_to_apply = 0;
}
return true;
}
bool TreeSocket::ChangeName(const std::string &prefix, std::deque<std::string> ¶ms)
{
if (params.size() < 1)
return true;
userrec* u = this->Instance->FindNick(prefix);
if (u)
{
u->ChangeName(params[0].c_str());
params[0] = ":" + params[0];
Utils->DoOneToAllButSender(prefix,"FNAME",params,u->server);
}
return true;
}
bool TreeSocket::Whois(const std::string &prefix, std::deque<std::string> ¶ms)
{
if (params.size() < 1)
return true;
userrec* u = this->Instance->FindNick(prefix);
if (u)
{
// an incoming request
if (params.size() == 1)
{
userrec* x = this->Instance->FindNick(params[0]);
if ((x) && (IS_LOCAL(x)))
{
userrec* x = this->Instance->FindNick(params[0]);
char signon[MAXBUF];
char idle[MAXBUF];
snprintf(signon, MAXBUF, "%lu", (unsigned long)x->signon);
snprintf(idle, MAXBUF, "%lu", (unsigned long)abs((x->idle_lastmsg) - Instance->Time(true)));
std::deque<std::string> par;
par.push_back(prefix);
par.push_back(signon);
par.push_back(idle);
// ours, we're done, pass it BACK
Utils->DoOneToOne(params[0], "IDLE", par, u->server);
}
else
{
// not ours pass it on
if (x)
Utils->DoOneToOne(prefix, "IDLE", params, x->server);
}
}
else if (params.size() == 3)
{
std::string who_did_the_whois = params[0];
userrec* who_to_send_to = this->Instance->FindNick(who_did_the_whois);
if ((who_to_send_to) && (IS_LOCAL(who_to_send_to)))
{
// an incoming reply to a whois we sent out
std::string nick_whoised = prefix;
unsigned long signon = atoi(params[1].c_str());
unsigned long idle = atoi(params[2].c_str());
if ((who_to_send_to) && (IS_LOCAL(who_to_send_to)))
{
do_whois(this->Instance, who_to_send_to, u, signon, idle, nick_whoised.c_str());
}
}
else
{
// not ours, pass it on
if (who_to_send_to)
Utils->DoOneToOne(prefix, "IDLE", params, who_to_send_to->server);
}
}
}
return true;
}
bool TreeSocket::Push(const std::string &prefix, std::deque<std::string> ¶ms)
{
if (params.size() < 2)
return true;
userrec* u = this->Instance->FindNick(params[0]);
if (!u)
return true;
if (IS_LOCAL(u))
{
u->Write(params[1]);
}
else
{
// continue the raw onwards
params[1] = ":" + params[1];
Utils->DoOneToOne(prefix,"PUSH",params,u->server);
}
return true;
}
bool TreeSocket::HandleSetTime(const std::string &prefix, std::deque<std::string> ¶ms)
{
if (!params.size() || !Utils->EnableTimeSync)
return true;
bool force = false;
if ((params.size() == 2) && (params[1] == "FORCE"))
force = true;
time_t them = atoi(params[0].c_str());
time_t us = Instance->Time(false);
time_t diff = them - us;
Utils->DoOneToAllButSender(prefix, "TIMESET", params, prefix);
if (force || (them != us))
{
time_t old = Instance->SetTimeDelta(diff);
Instance->Log(DEBUG, "TS (diff %d) from %s applied (old delta was %d)", diff, prefix.c_str(), old);
}
return true;
}
bool TreeSocket::Time(const std::string &prefix, std::deque<std::string> ¶ms)
{
// :source.server TIME remote.server sendernick
// :remote.server TIME source.server sendernick TS
if (params.size() == 2)
{
// someone querying our time?
if (this->Instance->Config->ServerName == params[0])
{
userrec* u = this->Instance->FindNick(params[1]);
if (u)
{
params.push_back(ConvToStr(Instance->Time(false)));
params[0] = prefix;
Utils->DoOneToOne(this->Instance->Config->ServerName,"TIME",params,params[0]);
}
}
else
{
// not us, pass it on
userrec* u = this->Instance->FindNick(params[1]);
if (u)
Utils->DoOneToOne(prefix,"TIME",params,params[0]);
}
}
else if (params.size() == 3)
{
// a response to a previous TIME
userrec* u = this->Instance->FindNick(params[1]);
if ((u) && (IS_LOCAL(u)))
{
time_t rawtime = atol(params[2].c_str());
struct tm * timeinfo;
timeinfo = localtime(&rawtime);
char tms[26];
snprintf(tms,26,"%s",asctime(timeinfo));
tms[24] = 0;
u->WriteServ("391 %s %s :%s",u->nick,prefix.c_str(),tms);
}
else
{
if (u)
Utils->DoOneToOne(prefix,"TIME",params,u->server);
}
}
return true;
}
bool TreeSocket::LocalPing(const std::string &prefix, std::deque<std::string> ¶ms)
{
if (params.size() < 1)
return true;
if (params.size() == 1)
{
std::string stufftobounce = params[0];
this->WriteLine(std::string(":")+this->Instance->Config->ServerName+" PONG "+stufftobounce);
return true;
}
else
{
std::string forwardto = params[1];
if (forwardto == this->Instance->Config->ServerName)
{
// this is a ping for us, send back PONG to the requesting server
params[1] = params[0];
params[0] = forwardto;
Utils->DoOneToOne(forwardto,"PONG",params,params[1]);
}
else
{
// not for us, pass it on :)
Utils->DoOneToOne(prefix,"PING",params,forwardto);
}
return true;
}
}
/** TODO: This creates a total mess of output and needs to really use irc::modestacker.
*/
bool TreeSocket::RemoveStatus(const std::string &prefix, std::deque<std::string> ¶ms)
{
if (params.size() < 1)
return true;
chanrec* c = Instance->FindChan(params[0]);
if (c)
{
for (char modeletter = 'A'; modeletter <= 'z'; modeletter++)
{
ModeHandler* mh = Instance->Modes->FindMode(modeletter, MODETYPE_CHANNEL);
if (mh)
mh->RemoveMode(c);
}
}
return true;
}
bool TreeSocket::RemoteServer(const std::string &prefix, std::deque<std::string> ¶ms)
{
if (params.size() < 4)
return false;
std::string servername = params[0];
std::string password = params[1];
// hopcount is not used for a remote server, we calculate this ourselves
std::string description = params[3];
TreeServer* ParentOfThis = Utils->FindServer(prefix);
if (!ParentOfThis)
{
this->SendError("Protocol error - Introduced remote server from unknown server "+prefix);
return false;
}
TreeServer* CheckDupe = Utils->FindServer(servername);
if (CheckDupe)
{
this->SendError("Server "+servername+" already exists!");
this->Instance->SNO->WriteToSnoMask('l',"Server \2"+servername+"\2 being introduced from \2" + prefix + "\2 denied, already exists. Closing link with " + prefix);
return false;
}
Link* lnk = Utils->FindLink(servername);
TreeServer* Node = new TreeServer(this->Utils,this->Instance,servername,description,ParentOfThis,NULL, lnk ? lnk->Hidden : false);
ParentOfThis->AddChild(Node);
params[3] = ":" + params[3];
Utils->SetRemoteBursting(Node, true);
Utils->DoOneToAllButSender(prefix,"SERVER",params,prefix);
this->Instance->SNO->WriteToSnoMask('l',"Server \002"+prefix+"\002 introduced server \002"+servername+"\002 ("+description+")");
return true;
}
bool TreeSocket::ComparePass(const std::string &ours, const std::string &theirs)
{
if ((!strncmp(ours.c_str(), "HMAC-SHA256:", 12)) || (!strncmp(theirs.c_str(), "HMAC-SHA256:", 12)))
{
/* One or both of us specified hmac sha256, but we don't have sha256 module loaded!
* We can't allow this password as valid.
*/
if (!Instance->FindModule("m_sha256.so") || !Utils->ChallengeResponse)
return false;
else
/* Straight string compare of hashes */
return ours == theirs;
}
else
/* Straight string compare of plaintext */
return ours == theirs;
}
bool TreeSocket::Outbound_Reply_Server(std::deque<std::string> ¶ms)
{
if (params.size() < 4)
return false;
irc::string servername = params[0].c_str();
std::string sname = params[0];
std::string password = params[1];
std::string description = params[3];
int hops = atoi(params[2].c_str());
this->InboundServerName = sname;
this->InboundDescription = description;
if (!sentcapab)
this->SendCapabilities();
if (hops)
{
this->SendError("Server too far away for authentication");
this->Instance->SNO->WriteToSnoMask('l',"Server connection from \2"+sname+"\2 denied, server is too far away for authentication");
return false;
}
for (std::vector<Link>::iterator x = Utils->LinkBlocks.begin(); x < Utils->LinkBlocks.end(); x++)
{
if ((x->Name == servername) && ((ComparePass(this->MakePass(x->RecvPass,this->GetOurChallenge()),password)) || (x->RecvPass == password && (this->GetTheirChallenge().empty()))))
{
TreeServer* CheckDupe = Utils->FindServer(sname);
if (CheckDupe)
{
this->SendError("Server "+sname+" already exists on server "+CheckDupe->GetParent()->GetName()+"!");
this->Instance->SNO->WriteToSnoMask('l',"Server connection from \2"+sname+"\2 denied, already exists on server "+CheckDupe->GetParent()->GetName());
return false;
}
// Begin the sync here. this kickstarts the
// other side, waiting in WAIT_AUTH_2 state,
// into starting their burst, as it shows
// that we're happy.
this->LinkState = CONNECTED;
// we should add the details of this server now
// to the servers tree, as a child of the root
// node.
TreeServer* Node = new TreeServer(this->Utils,this->Instance,sname,description,Utils->TreeRoot,this,x->Hidden);
Utils->TreeRoot->AddChild(Node);
params[3] = ":" + params[3];
Utils->DoOneToAllButSender(Utils->TreeRoot->GetName(),"SERVER",params,sname);
this->bursting = true;
this->DoBurst(Node);
return true;
}
}
this->SendError("Invalid credentials");
this->Instance->SNO->WriteToSnoMask('l',"Server connection from \2"+sname+"\2 denied, invalid link credentials");
return false;
}
bool TreeSocket::Inbound_Server(std::deque<std::string> ¶ms)
{
if (params.size() < 4)
return false;
irc::string servername = params[0].c_str();
std::string sname = params[0];
std::string password = params[1];
std::string description = params[3];
int hops = atoi(params[2].c_str());
this->InboundServerName = sname;
this->InboundDescription = description;
if (!sentcapab)
this->SendCapabilities();
if (hops)
{
this->SendError("Server too far away for authentication");
this->Instance->SNO->WriteToSnoMask('l',"Server connection from \2"+sname+"\2 denied, server is too far away for authentication");
return false;
}
for (std::vector<Link>::iterator x = Utils->LinkBlocks.begin(); x < Utils->LinkBlocks.end(); x++)
{
if ((x->Name == servername) && ((ComparePass(this->MakePass(x->RecvPass,this->GetOurChallenge()),password) || x->RecvPass == password && (this->GetTheirChallenge().empty()))))
{
/* First check for instances of the server that are waiting between the inbound and outbound SERVER command */
TreeSocket* CheckDupeSocket = Utils->FindBurstingServer(sname);
if (CheckDupeSocket)
{
/* If we find one, we abort the link to prevent a race condition */
this->SendError("Negotiation collision");
this->Instance->SNO->WriteToSnoMask('l',"Server connection from \2"+sname+"\2 denied, already exists in a negotiating state.");
CheckDupeSocket->SendError("Negotiation collision");
Instance->SE->DelFd(CheckDupeSocket);
CheckDupeSocket->Close();
delete CheckDupeSocket;
return false;
}
/* Now check for fully initialized instances of the server */
TreeServer* CheckDupe = Utils->FindServer(sname);
if (CheckDupe)
{
this->SendError("Server "+sname+" already exists on server "+CheckDupe->GetParent()->GetName()+"!");
this->Instance->SNO->WriteToSnoMask('l',"Server connection from \2"+sname+"\2 denied, already exists on server "+CheckDupe->GetParent()->GetName());
return false;
}
this->Instance->SNO->WriteToSnoMask('l',"Verified incoming server connection from \002"+sname+"\002["+(x->HiddenFromStats ? "<hidden>" : this->GetIP())+"] ("+description+")");
if (this->Hook)
{
std::string name = InspSocketNameRequest((Module*)Utils->Creator, this->Hook).Send();
this->Instance->SNO->WriteToSnoMask('l',"Connection from \2"+sname+"\2["+(x->HiddenFromStats ? "<hidden>" : this->GetIP())+"] using transport \2"+name+"\2");
}
Utils->AddBurstingServer(sname,this);
// this is good. Send our details: Our server name and description and hopcount of 0,
// along with the sendpass from this block.
this->WriteLine(std::string("SERVER ")+this->Instance->Config->ServerName+" "+this->MakePass(x->SendPass, this->GetTheirChallenge())+" 0 :"+this->Instance->Config->ServerDesc);
// move to the next state, we are now waiting for THEM.
this->LinkState = WAIT_AUTH_2;
return true;
}
}
this->SendError("Invalid credentials");
this->Instance->SNO->WriteToSnoMask('l',"Server connection from \2"+sname+"\2 denied, invalid link credentials");
return false;
}
void TreeSocket::Split(const std::string &line, std::deque<std::string> &n)
{
n.clear();
irc::tokenstream tokens(line);
std::string param;
while (tokens.GetToken(param))
{
if (!param.empty())
n.push_back(param);
}
return;
}
bool TreeSocket::ProcessLine(std::string &line)
{
std::deque<std::string> params;
irc::string command;
std::string prefix;
line = line.substr(0, line.find_first_of("\r\n"));
if (line.empty())
return true;
Instance->Log(DEBUG, "S[%d] <- %s", this->GetFd(), line.c_str());
this->Split(line.c_str(),params);
if (params.empty())
return true;
if ((params[0][0] == ':') && (params.size() > 1))
{
prefix = params[0].substr(1);
params.pop_front();
}
command = params[0].c_str();
params.pop_front();
switch (this->LinkState)
{
TreeServer* Node;
case WAIT_AUTH_1:
// Waiting for SERVER command from remote server. Server initiating
// the connection sends the first SERVER command, listening server
// replies with theirs if its happy, then if the initiator is happy,
// it starts to send its net sync, which starts the merge, otherwise
// it sends an ERROR.
if (command == "PASS")
{
/* Silently ignored */
}
else if (command == "SERVER")
{
return this->Inbound_Server(params);
}
else if (command == "ERROR")
{
return this->Error(params);
}
else if (command == "USER")
{
this->SendError("Client connections to this port are prohibited.");
return false;
}
else if (command == "CAPAB")
{
return this->Capab(params);
}
else if ((command == "U") || (command == "S"))
{
this->SendError("Cannot use the old-style mesh linking protocol with m_spanningtree.so!");
return false;
}
else
{
irc::string error = "Invalid command in negotiation phase: " + command;
this->SendError(assign(error));
return false;
}
break;
case WAIT_AUTH_2:
// Waiting for start of other side's netmerge to say they liked our
// password.
if (command == "SERVER")
{
// cant do this, they sent it to us in the WAIT_AUTH_1 state!
// silently ignore.
return true;
}
else if ((command == "U") || (command == "S"))
{
this->SendError("Cannot use the old-style mesh linking protocol with m_spanningtree.so!");
return false;
}
else if (command == "BURST")
{
if (params.size() && Utils->EnableTimeSync)
{
bool we_have_delta = (Instance->Time(false) != Instance->Time(true));
time_t them = atoi(params[0].c_str());
time_t delta = them - Instance->Time(false);
if ((delta < -300) || (delta > 300))
{
Instance->SNO->WriteToSnoMask('l',"\2ERROR\2: Your clocks are out by %d seconds (this is more than five minutes). Link aborted, \2PLEASE SYNC YOUR CLOCKS!\2",abs(delta));
SendError("Your clocks are out by "+ConvToStr(abs(delta))+" seconds (this is more than five minutes). Link aborted, PLEASE SYNC YOUR CLOCKS!");
return false;
}
else if ((delta < -30) || (delta > 30))
{
Instance->SNO->WriteToSnoMask('l',"\2WARNING\2: Your clocks are out by %d seconds. Please consider synching your clocks.", abs(delta));
}
if (!Utils->MasterTime && !we_have_delta)
{
this->Instance->SetTimeDelta(delta);
// Send this new timestamp to any other servers
Utils->DoOneToMany(Utils->TreeRoot->GetName(), "TIMESET", params);
}
}
this->LinkState = CONNECTED;
Link* lnk = Utils->FindLink(InboundServerName);
Node = new TreeServer(this->Utils,this->Instance, InboundServerName, InboundDescription, Utils->TreeRoot, this, lnk ? lnk->Hidden : false);
Utils->DelBurstingServer(this);
Utils->TreeRoot->AddChild(Node);
params.clear();
params.push_back(InboundServerName);
params.push_back("*");
params.push_back("1");
params.push_back(":"+InboundDescription);
Utils->DoOneToAllButSender(Utils->TreeRoot->GetName(),"SERVER",params,InboundServerName);
this->bursting = true;
this->DoBurst(Node);
}
else if (command == "ERROR")
{
return this->Error(params);
}
else if (command == "CAPAB")
{
return this->Capab(params);
}
break;
case LISTENER:
this->SendError("Internal error -- listening socket accepted its own descriptor!!!");
return false;
break;
case CONNECTING:
if (command == "SERVER")
{
// another server we connected to, which was in WAIT_AUTH_1 state,
// has just sent us their credentials. If we get this far, theyre
// happy with OUR credentials, and they are now in WAIT_AUTH_2 state.
// if we're happy with this, we should send our netburst which
// kickstarts the merge.
return this->Outbound_Reply_Server(params);
}
else if (command == "ERROR")
{
return this->Error(params);
}
else if (command == "CAPAB")
{
return this->Capab(params);
}
break;
case CONNECTED:
// This is the 'authenticated' state, when all passwords
// have been exchanged and anything past this point is taken
// as gospel.
if (!prefix.empty())
{
std::string direction = prefix;
userrec* t = this->Instance->FindNick(prefix);
if (t)
{
direction = t->server;
}
TreeServer* route_back_again = Utils->BestRouteTo(direction);
if ((!route_back_again) || (route_back_again->GetSocket() != this))
{
if (route_back_again)
Instance->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) + 60);
route_back_again->SetPingFlag();
}
else
{
prefix = this->GetName();
}
if ((command == "MODE") && (params.size() >= 2))
{
chanrec* channel = Instance->FindChan(params[0]);
if (channel)
{
userrec* x = Instance->FindNick(prefix);
if (x)
{
if (warned.find(x->server) == warned.end())
{
Instance->Log(DEFAULT,"WARNING: I revceived modes '%s' from another server '%s'. This is not compliant with InspIRCd. Please check that server for bugs.", params[1].c_str(), x->server);
Instance->SNO->WriteToSnoMask('d', "WARNING: The server %s is sending nonstandard modes: '%s MODE %s' where FMODE should be used, and may cause desyncs.", x->server, x->nick, params[1].c_str());
warned[x->server] = x->nick;
}
}
}
}
if (command == "SVSMODE")
{
/* Services expects us to implement
* SVSMODE. In inspircd its the same as
* MODE anyway.
*/
command = "MODE";
}
std::string target;
/* Yes, know, this is a mess. Its reasonably fast though as we're
* working with std::string here.
*/
if ((command == "NICK") && (params.size() >= 8))
{
return this->IntroduceClient(prefix,params);
}
else if (command == "FJOIN")
{
TreeServer* ServerSource = Utils->FindServer(prefix);
if (ServerSource)
Utils->SetRemoteBursting(ServerSource, false);
return this->ForceJoin(prefix,params);
}
else if (command == "STATS")
{
return this->Stats(prefix, params);
}
else if (command == "MOTD")
{
return this->Motd(prefix, params);
}
else if (command == "KILL" && Utils->IsServer(prefix))
{
return this->RemoteKill(prefix,params);
}
else if (command == "MODULES")
{
return this->Modules(prefix, params);
}
else if (command == "ADMIN")
{
return this->Admin(prefix, params);
}
else if (command == "SERVER")
{
return this->RemoteServer(prefix,params);
}
else if (command == "ERROR")
{
return this->Error(params);
}
else if (command == "OPERTYPE")
{
return this->OperType(prefix,params);
}
else if (command == "FMODE")
{
TreeServer* ServerSource = Utils->FindServer(prefix);
if (ServerSource)
Utils->SetRemoteBursting(ServerSource, false);
return this->ForceMode(prefix,params);
}
else if (command == "FTOPIC")
{
return this->ForceTopic(prefix,params);
}
else if (command == "REHASH")
{
return this->RemoteRehash(prefix,params);
}
else if (command == "METADATA")
{
return this->MetaData(prefix,params);
}
else if (command == "REMSTATUS")
{
return this->RemoveStatus(prefix,params);
}
else if (command == "PING")
{
if (prefix.empty())
prefix = this->GetName();
/*
* We just got a ping from a server that's bursting.
* This can't be right, so set them to not bursting, and
* apply their lines.
*/
TreeServer* ServerSource = Utils->FindServer(prefix);
if (ServerSource)
Utils->SetRemoteBursting(ServerSource, false);
if (this->bursting)
{
this->bursting = false;
Instance->XLines->apply_lines(Utils->lines_to_apply);
Utils->lines_to_apply = 0;
}
return this->LocalPing(prefix,params);
}
else if (command == "PONG")
{
if (prefix.empty())
prefix = this->GetName();
/*
* We just got a pong from a server that's bursting.
* This can't be right, so set them to not bursting, and
* apply their lines.
*/
TreeServer* ServerSource = Utils->FindServer(prefix);
if (ServerSource)
Utils->SetRemoteBursting(ServerSource, false);
if (this->bursting)
{
this->bursting = false;
Instance->XLines->apply_lines(Utils->lines_to_apply);
Utils->lines_to_apply = 0;
}
return this->LocalPong(prefix,params);
}
else if (command == "VERSION")
{
return this->ServerVersion(prefix,params);
}
else if (command == "FHOST")
{
return this->ChangeHost(prefix,params);
}
else if (command == "FNAME")
{
return this->ChangeName(prefix,params);
}
else if (command == "ADDLINE")
{
TreeServer* ServerSource = Utils->FindServer(prefix);
if (ServerSource)
Utils->SetRemoteBursting(ServerSource, false);
return this->AddLine(prefix,params);
}
else if (command == "SVSNICK")
{
if (prefix.empty())
{
prefix = this->GetName();
}
return this->ForceNick(prefix,params);
}
else if (command == "OPERQUIT")
{
return this->OperQuit(prefix,params);
}
else if (command == "IDLE")
{
return this->Whois(prefix,params);
}
else if (command == "PUSH")
{
return this->Push(prefix,params);
}
else if (command == "TIMESET")
{
return this->HandleSetTime(prefix, params);
}
else if (command == "TIME")
{
return this->Time(prefix,params);
}
else if ((command == "KICK") && (Utils->IsServer(prefix)))
{
std::string sourceserv = this->myhost;
if (params.size() == 3)
{
userrec* user = this->Instance->FindNick(params[1]);
chanrec* chan = this->Instance->FindChan(params[0]);
if (user && chan)
{
if (!chan->ServerKickUser(user, params[2].c_str(), false))
/* Yikes, the channels gone! */
delete chan;
}
}
if (!this->InboundServerName.empty())
{
sourceserv = this->InboundServerName;
}
return Utils->DoOneToAllButSenderRaw(line,sourceserv,prefix,command,params);
}
else if (command == "SVSJOIN")
{
if (prefix.empty())
{
prefix = this->GetName();
}
return this->ServiceJoin(prefix,params);
}
else if (command == "SQUIT")
{
if (params.size() == 2)
{
this->Squit(Utils->FindServer(params[0]),params[1]);
}
return true;
}
else if (command == "OPERNOTICE")
{
std::string sourceserv = this->myhost;
if (!this->InboundServerName.empty())
sourceserv = this->InboundServerName;
if (params.size() >= 1)
Instance->WriteOpers("*** From " + sourceserv + ": " + params[0]);
return Utils->DoOneToAllButSenderRaw(line, sourceserv, prefix, command, params);
}
else if (command == "MODENOTICE")
{
std::string sourceserv = this->myhost;
if (!this->InboundServerName.empty())
sourceserv = this->InboundServerName;
if (params.size() >= 2)
{
Instance->WriteMode(params[0].c_str(), WM_AND, "*** From %s: %s", sourceserv.c_str(), params[1].c_str());
}
return Utils->DoOneToAllButSenderRaw(line, sourceserv, prefix, command, params);
}
else if (command == "SNONOTICE")
{
std::string sourceserv = this->myhost;
if (!this->InboundServerName.empty())
sourceserv = this->InboundServerName;
if (params.size() >= 2)
{
Instance->SNO->WriteToSnoMask(*(params[0].c_str()), "From " + sourceserv + ": "+ params[1]);
}
return Utils->DoOneToAllButSenderRaw(line, sourceserv, prefix, command, params);
}
else if (command == "ENDBURST")
{
this->bursting = false;
Instance->XLines->apply_lines(Utils->lines_to_apply);
Utils->lines_to_apply = 0;
std::string sourceserv = this->myhost;
if (!this->InboundServerName.empty())
sourceserv = this->InboundServerName;
this->Instance->SNO->WriteToSnoMask('l',"Received end of netburst from \2%s\2",sourceserv.c_str());
Event rmode((char*)sourceserv.c_str(), (Module*)Utils->Creator, "new_server");
rmode.Send(Instance);
return true;
}
else
{
// not a special inter-server command.
// Emulate the actual user doing the command,
// this saves us having a huge ugly parser.
userrec* who = this->Instance->FindNick(prefix);
std::string sourceserv = this->myhost;
if (!this->InboundServerName.empty())
{
sourceserv = this->InboundServerName;
}
if ((!who) && (command == "MODE"))
{
if (Utils->IsServer(prefix))
{
const char* modelist[127];
for (size_t i = 0; i < params.size(); i++)
modelist[i] = params[i].c_str();
userrec* fake = new userrec(Instance);
fake->SetFd(FD_MAGIC_NUMBER);
this->Instance->SendMode(modelist, params.size(), fake);
delete fake;
/* Hot potato! pass it on! */
return Utils->DoOneToAllButSenderRaw(line,sourceserv,prefix,command,params);
}
}
if (who)
{
if ((command == "NICK") && (params.size() > 0))
{
/* On nick messages, check that the nick doesnt
* already exist here. If it does, kill their copy,
* and our copy.
*/
userrec* x = this->Instance->FindNick(params[0]);
if ((x) && (x != who))
{
std::deque<std::string> p;
p.push_back(params[0]);
p.push_back("Nickname collision ("+prefix+" -> "+params[0]+")");
Utils->DoOneToMany(this->Instance->Config->ServerName,"KILL",p);
p.clear();
p.push_back(prefix);
p.push_back("Nickname collision");
Utils->DoOneToMany(this->Instance->Config->ServerName,"KILL",p);
userrec::QuitUser(this->Instance,x,"Nickname collision ("+prefix+" -> "+params[0]+")");
userrec* y = this->Instance->FindNick(prefix);
if (y)
{
userrec::QuitUser(this->Instance,y,"Nickname collision");
}
return Utils->DoOneToAllButSenderRaw(line,sourceserv,prefix,command,params);
}
}
// its a user
target = who->server;
const char* strparams[127];
for (unsigned int q = 0; q < params.size(); q++)
{
strparams[q] = params[q].c_str();
}
switch (this->Instance->CallCommandHandler(command.c_str(), strparams, params.size(), who))
{
case CMD_INVALID:
this->SendError("Unrecognised command '"+std::string(command.c_str())+"' -- possibly loaded mismatched modules");
return false;
break;
case CMD_FAILURE:
return true;
break;
default:
/* CMD_SUCCESS and CMD_USER_DELETED fall through here */
break;
}
}
else
{
// its not a user. Its either a server, or somethings screwed up.
if (Utils->IsServer(prefix))
target = this->Instance->Config->ServerName;
else
return true;
}
return Utils->DoOneToAllButSenderRaw(line,sourceserv,prefix,command,params);
}
return true;
break;
}
return true;
}
std::string TreeSocket::GetName()
{
std::string sourceserv = this->myhost;
if (!this->InboundServerName.empty())
{
sourceserv = this->InboundServerName;
}
return sourceserv;
}
void TreeSocket::OnTimeout()
{
if (this->LinkState == CONNECTING)
{
this->Instance->SNO->WriteToSnoMask('l',"CONNECT: Connection to \002"+myhost+"\002 timed out.");
Link* MyLink = Utils->FindLink(myhost);
if (MyLink)
Utils->DoFailOver(MyLink);
}
}
void TreeSocket::OnClose()
{
// Connection closed.
// If the connection is fully up (state CONNECTED)
// then propogate a netsplit to all peers.
std::string quitserver = this->myhost;
if (!this->InboundServerName.empty())
{
quitserver = this->InboundServerName;
}
TreeServer* s = Utils->FindServer(quitserver);
if (s)
{
Squit(s,"Remote host closed the connection");
}
if (!quitserver.empty())
{
this->Instance->SNO->WriteToSnoMask('l',"Connection to '\2%s\2' failed.",quitserver.c_str());
time_t server_uptime = Instance->Time() - this->age;
if (server_uptime)
Instance->SNO->WriteToSnoMask('l',"Connection to '\2%s\2' was established for %s", quitserver.c_str(), Utils->Creator->TimeToStr(server_uptime).c_str());
}
}
int TreeSocket::OnIncomingConnection(int newsock, char* ip)
{
/* To prevent anyone from attempting to flood opers/DDoS by connecting to the server port,
* or discovering if this port is the server port, we don't allow connections from any
* IPs for which we don't have a link block.
*/
bool found = false;
found = (std::find(Utils->ValidIPs.begin(), Utils->ValidIPs.end(), ip) != Utils->ValidIPs.end());
if (!found)
{
for (vector<std::string>::iterator i = Utils->ValidIPs.begin(); i != Utils->ValidIPs.end(); i++)
if (irc::sockets::MatchCIDR(ip, (*i).c_str()))
found = true;
if (!found)
{
this->Instance->SNO->WriteToSnoMask('l',"Server connection from %s denied (no link blocks with that IP address)", ip);
close(newsock);
return false;
}
}
TreeSocket* s = new TreeSocket(this->Utils, this->Instance, newsock, ip, this->Hook);
s = s; /* Whinge whinge whinge, thats all GCC ever does. */
return true;
}
\ No newline at end of file +/* +------------------------------------+ + * | 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 "users.h" +#include "channels.h" +#include "modules.h" +#include "commands/cmd_whois.h" +#include "commands/cmd_stats.h" +#include "socket.h" +#include "wildcard.h" +#include "xline.h" +#include "transport.h" +#include "socketengine.h" + +#include "m_spanningtree/main.h" +#include "m_spanningtree/utils.h" +#include "m_spanningtree/treeserver.h" +#include "m_spanningtree/link.h" +#include "m_spanningtree/treesocket.h" +#include "m_spanningtree/resolvers.h" +#include "m_spanningtree/handshaketimer.h" + +/* $ModDep: m_spanningtree/timesynctimer.h m_spanningtree/resolvers.h m_spanningtree/main.h m_spanningtree/utils.h m_spanningtree/treeserver.h m_spanningtree/link.h m_spanningtree/treesocket.h */ + +static std::map<std::string, std::string> warned; /* Server names that have had protocol violation warnings displayed for them */ + +int TreeSocket::WriteLine(std::string line) +{ + Instance->Log(DEBUG, "S[%d] -> %s", this->GetFd(), line.c_str()); + line.append("\r\n"); + return this->Write(line); +} + + +/* Handle ERROR command */ +bool TreeSocket::Error(std::deque<std::string> ¶ms) +{ + if (params.size() < 1) + return false; + this->Instance->SNO->WriteToSnoMask('l',"ERROR from %s: %s",(!InboundServerName.empty() ? InboundServerName.c_str() : myhost.c_str()),params[0].c_str()); + /* we will return false to cause the socket to close. */ + return false; +} + +bool TreeSocket::Modules(const std::string &prefix, std::deque<std::string> ¶ms) +{ + if (params.empty()) + return true; + + if (!this->Instance->MatchText(this->Instance->Config->ServerName, params[0])) + { + /* Pass it on, not for us */ + Utils->DoOneToOne(prefix, "MODULES", params, params[0]); + return true; + } + + char strbuf[MAXBUF]; + std::deque<std::string> par; + par.push_back(prefix); + par.push_back(""); + + userrec* source = this->Instance->FindNick(prefix); + if (!source) + return true; + + for (unsigned int i = 0; i < Instance->Config->module_names.size(); i++) + { + Version V = Instance->modules[i]->GetVersion(); + char modulename[MAXBUF]; + char flagstate[MAXBUF]; + *flagstate = 0; + if (V.Flags & VF_STATIC) + strlcat(flagstate,", static",MAXBUF); + if (V.Flags & VF_VENDOR) + strlcat(flagstate,", vendor",MAXBUF); + if (V.Flags & VF_COMMON) + strlcat(flagstate,", common",MAXBUF); + if (V.Flags & VF_SERVICEPROVIDER) + strlcat(flagstate,", service provider",MAXBUF); + if (!flagstate[0]) + strcpy(flagstate," <no flags>"); + strlcpy(modulename,Instance->Config->module_names[i].c_str(),256); + if (*source->oper) + { + snprintf(strbuf, MAXBUF, "::%s 900 %s :0x%08lx %d.%d.%d.%d %s (%s)",Instance->Config->ServerName,source->nick,(long unsigned int)Instance->modules[i],V.Major,V.Minor,V.Revision,V.Build,ServerConfig::CleanFilename(modulename),flagstate+2); + } + else + { + snprintf(strbuf, MAXBUF, "::%s 900 %s :%s",Instance->Config->ServerName,source->nick,ServerConfig::CleanFilename(modulename)); + } + par[1] = strbuf; + Utils->DoOneToOne(Instance->Config->ServerName, "PUSH", par, source->server); + } + snprintf(strbuf, MAXBUF, "::%s 901 %s :End of MODULES list", Instance->Config->ServerName, source->nick); + par[1] = strbuf; + Utils->DoOneToOne(Instance->Config->ServerName, "PUSH", par, source->server); + return true; +} + +/** remote MOTD. leet, huh? */ +bool TreeSocket::Motd(const std::string &prefix, std::deque<std::string> ¶ms) +{ + if (params.size() > 0) + { + if (this->Instance->MatchText(this->Instance->Config->ServerName, params[0])) + { + /* It's for our server */ + string_list results; + userrec* source = this->Instance->FindNick(prefix); + + if (source) + { + std::deque<std::string> par; + par.push_back(prefix); + par.push_back(""); + + if (!Instance->Config->MOTD.size()) + { + par[1] = std::string("::")+Instance->Config->ServerName+" 422 "+source->nick+" :Message of the day file is missing."; + Utils->DoOneToOne(this->Instance->Config->ServerName, "PUSH",par, source->server); + return true; + } + + par[1] = std::string("::")+Instance->Config->ServerName+" 375 "+source->nick+" :"+Instance->Config->ServerName+" message of the day"; + Utils->DoOneToOne(this->Instance->Config->ServerName, "PUSH",par, source->server); + + for (unsigned int i = 0; i < Instance->Config->MOTD.size(); i++) + { + par[1] = std::string("::")+Instance->Config->ServerName+" 372 "+source->nick+" :- "+Instance->Config->MOTD[i]; + Utils->DoOneToOne(this->Instance->Config->ServerName, "PUSH",par, source->server); + } + + par[1] = std::string("::")+Instance->Config->ServerName+" 376 "+source->nick+" End of message of the day."; + Utils->DoOneToOne(this->Instance->Config->ServerName, "PUSH",par, source->server); + } + } + else + { + /* Pass it on */ + userrec* source = this->Instance->FindNick(prefix); + if (source) + Utils->DoOneToOne(prefix, "MOTD", params, params[0]); + } + } + return true; +} + +/** remote ADMIN. leet, huh? */ +bool TreeSocket::Admin(const std::string &prefix, std::deque<std::string> ¶ms) +{ + if (params.size() > 0) + { + if (this->Instance->MatchText(this->Instance->Config->ServerName, params[0])) + { + /* It's for our server */ + string_list results; + userrec* source = this->Instance->FindNick(prefix); + if (source) + { + std::deque<std::string> par; + par.push_back(prefix); + par.push_back(""); + par[1] = std::string("::")+Instance->Config->ServerName+" 256 "+source->nick+" :Administrative info for "+Instance->Config->ServerName; + Utils->DoOneToOne(this->Instance->Config->ServerName, "PUSH",par, source->server); + par[1] = std::string("::")+Instance->Config->ServerName+" 257 "+source->nick+" :Name - "+Instance->Config->AdminName; + Utils->DoOneToOne(this->Instance->Config->ServerName, "PUSH",par, source->server); + par[1] = std::string("::")+Instance->Config->ServerName+" 258 "+source->nick+" :Nickname - "+Instance->Config->AdminNick; + Utils->DoOneToOne(this->Instance->Config->ServerName, "PUSH",par, source->server); + par[1] = std::string("::")+Instance->Config->ServerName+" 258 "+source->nick+" :E-Mail - "+Instance->Config->AdminEmail; + Utils->DoOneToOne(this->Instance->Config->ServerName, "PUSH",par, source->server); + } + } + else + { + /* Pass it on */ + userrec* source = this->Instance->FindNick(prefix); + if (source) + Utils->DoOneToOne(prefix, "ADMIN", params, params[0]); + } + } + return true; +} + +bool TreeSocket::Stats(const std::string &prefix, std::deque<std::string> ¶ms) +{ + /* Get the reply to a STATS query if it matches this servername, + * and send it back as a load of PUSH queries + */ + if (params.size() > 1) + { + if (this->Instance->MatchText(this->Instance->Config->ServerName, params[1])) + { + /* It's for our server */ + string_list results; + userrec* source = this->Instance->FindNick(prefix); + if (source) + { + std::deque<std::string> par; + par.push_back(prefix); + par.push_back(""); + DoStats(this->Instance, *(params[0].c_str()), source, results); + for (size_t i = 0; i < results.size(); i++) + { + par[1] = "::" + results[i]; + Utils->DoOneToOne(this->Instance->Config->ServerName, "PUSH",par, source->server); + } + } + } + else + { + /* Pass it on */ + userrec* source = this->Instance->FindNick(prefix); + if (source) + Utils->DoOneToOne(prefix, "STATS", params, params[1]); + } + } + return true; +} + + +/** Because the core won't let users or even SERVERS set +o, + * we use the OPERTYPE command to do this. + */ +bool TreeSocket::OperType(const std::string &prefix, std::deque<std::string> ¶ms) +{ + if (params.size() != 1) + return true; + std::string opertype = params[0]; + userrec* u = this->Instance->FindNick(prefix); + if (u) + { + u->modes[UM_OPERATOR] = 1; + this->Instance->all_opers.push_back(u); + strlcpy(u->oper,opertype.c_str(),NICKMAX-1); + Utils->DoOneToAllButSender(u->nick,"OPERTYPE",params,u->server); + this->Instance->SNO->WriteToSnoMask('o',"From %s: User %s (%s@%s) is now an IRC operator of type %s",u->server, u->nick,u->ident,u->host,irc::Spacify(opertype.c_str())); + } + return true; +} + +/** Because Andy insists that services-compatible servers must + * implement SVSNICK and SVSJOIN, that's exactly what we do :p + */ +bool TreeSocket::ForceNick(const std::string &prefix, std::deque<std::string> ¶ms) +{ + if (params.size() < 3) + return true; + + userrec* u = this->Instance->FindNick(params[0]); + + if (u) + { + Utils->DoOneToAllButSender(prefix,"SVSNICK",params,prefix); + if (IS_LOCAL(u)) + { + std::deque<std::string> par; + par.push_back(params[1]); + if (!u->ForceNickChange(params[1].c_str())) + { + userrec::QuitUser(this->Instance, u, "Nickname collision"); + return true; + } + u->age = atoi(params[2].c_str()); + } + } + return true; +} + +bool TreeSocket::OperQuit(const std::string &prefix, std::deque<std::string> ¶ms) +{ + if (params.size() < 1) + return true; + + userrec* u = this->Instance->FindNick(prefix); + + if (u) + { + u->SetOperQuit(params[0]); + params[0] = ":" + params[0]; + Utils->DoOneToAllButSender(prefix,"OPERQUIT",params,prefix); + } + return true; +} + +bool TreeSocket::ServiceJoin(const std::string &prefix, std::deque<std::string> ¶ms) +{ + if (params.size() < 2) + return true; + + userrec* u = this->Instance->FindNick(params[0]); + + if (u) + { + /* only join if it's local, otherwise just pass it on! */ + if (IS_LOCAL(u)) + chanrec::JoinUser(this->Instance, u, params[1].c_str(), false, "", Instance->Time()); + Utils->DoOneToAllButSender(prefix,"SVSJOIN",params,prefix); + } + return true; +} + +bool TreeSocket::RemoteRehash(const std::string &prefix, std::deque<std::string> ¶ms) +{ + if (params.size() < 1) + return false; + + std::string servermask = params[0]; + + if (this->Instance->MatchText(this->Instance->Config->ServerName,servermask)) + { + this->Instance->SNO->WriteToSnoMask('l',"Remote rehash initiated by \002"+prefix+"\002."); + this->Instance->RehashServer(); + Utils->ReadConfiguration(false); + InitializeDisabledCommands(Instance->Config->DisabledCommands, Instance); + } + Utils->DoOneToAllButSender(prefix,"REHASH",params,prefix); + return true; +} + +bool TreeSocket::RemoteKill(const std::string &prefix, std::deque<std::string> ¶ms) +{ + if (params.size() != 2) + return true; + + userrec* who = this->Instance->FindNick(params[0]); + + if (who) + { + /* Prepend kill source, if we don't have one */ + if (*(params[1].c_str()) != '[') + { + params[1] = "[" + prefix + "] Killed (" + params[1] +")"; + } + std::string reason = params[1]; + params[1] = ":" + params[1]; + Utils->DoOneToAllButSender(prefix,"KILL",params,prefix); + // NOTE: This is safe with kill hiding on, as RemoteKill is only reached if we have a server prefix. + // in short this is not executed for USERS. + who->Write(":%s KILL %s :%s (%s)", prefix.c_str(), who->nick, prefix.c_str(), reason.c_str()); + userrec::QuitUser(this->Instance,who,reason); + } + return true; +} + +bool TreeSocket::LocalPong(const std::string &prefix, std::deque<std::string> ¶ms) +{ + if (params.size() < 1) + return true; + + if (params.size() == 1) + { + TreeServer* ServerSource = Utils->FindServer(prefix); + if (ServerSource) + { + ServerSource->SetPingFlag(); + ServerSource->rtt = Instance->Time() - ServerSource->LastPing; + } + } + else + { + std::string forwardto = params[1]; + if (forwardto == this->Instance->Config->ServerName) + { + /* + * this is a PONG for us + * if the prefix is a user, check theyre local, and if they are, + * dump the PONG reply back to their fd. If its a server, do nowt. + * Services might want to send these s->s, but we dont need to yet. + */ + userrec* u = this->Instance->FindNick(prefix); + if (u) + { + u->WriteServ("PONG %s %s",params[0].c_str(),params[1].c_str()); + } + } + else + { + // not for us, pass it on :) + Utils->DoOneToOne(prefix,"PONG",params,forwardto); + } + } + + return true; +} + +bool TreeSocket::MetaData(const std::string &prefix, std::deque<std::string> ¶ms) +{ + if (params.size() < 2) + return true; + else if (params.size() < 3) + params.push_back(""); + TreeServer* ServerSource = Utils->FindServer(prefix); + if (ServerSource) + { + Utils->SetRemoteBursting(ServerSource, false); + + if (params[0] == "*") + { + FOREACH_MOD_I(this->Instance,I_OnDecodeMetaData,OnDecodeMetaData(TYPE_OTHER,NULL,params[1],params[2])); + } + else if (*(params[0].c_str()) == '#') + { + chanrec* c = this->Instance->FindChan(params[0]); + if (c) + { + FOREACH_MOD_I(this->Instance,I_OnDecodeMetaData,OnDecodeMetaData(TYPE_CHANNEL,c,params[1],params[2])); + } + } + else if (*(params[0].c_str()) != '#') + { + userrec* u = this->Instance->FindNick(params[0]); + if (u) + { + FOREACH_MOD_I(this->Instance,I_OnDecodeMetaData,OnDecodeMetaData(TYPE_USER,u,params[1],params[2])); + } + } + } + + params[2] = ":" + params[2]; + Utils->DoOneToAllButSender(prefix,"METADATA",params,prefix); + return true; +} + +bool TreeSocket::ServerVersion(const std::string &prefix, std::deque<std::string> ¶ms) +{ + if (params.size() < 1) + return true; + + TreeServer* ServerSource = Utils->FindServer(prefix); + + if (ServerSource) + { + ServerSource->SetVersion(params[0]); + } + params[0] = ":" + params[0]; + Utils->DoOneToAllButSender(prefix,"VERSION",params,prefix); + return true; +} + +bool TreeSocket::ChangeHost(const std::string &prefix, std::deque<std::string> ¶ms) +{ + if (params.size() < 1) + return true; + userrec* u = this->Instance->FindNick(prefix); + + if (u) + { + u->ChangeDisplayedHost(params[0].c_str()); + Utils->DoOneToAllButSender(prefix,"FHOST",params,u->server); + } + return true; +} + +bool TreeSocket::AddLine(const std::string &prefix, std::deque<std::string> ¶ms) +{ + if (params.size() < 6) + return true; + bool propogate = false; + if (!this->bursting) + Utils->lines_to_apply = 0; + switch (*(params[0].c_str())) + { + case 'Z': + propogate = Instance->XLines->add_zline(atoi(params[4].c_str()), params[2].c_str(), params[5].c_str(), params[1].c_str()); + Instance->XLines->zline_set_creation_time(params[1].c_str(), atoi(params[3].c_str())); + if (propogate) + Utils->lines_to_apply |= APPLY_ZLINES; + break; + case 'Q': + propogate = Instance->XLines->add_qline(atoi(params[4].c_str()), params[2].c_str(), params[5].c_str(), params[1].c_str()); + Instance->XLines->qline_set_creation_time(params[1].c_str(), atoi(params[3].c_str())); + if (propogate) + Utils->lines_to_apply |= APPLY_QLINES; + break; + case 'E': + propogate = Instance->XLines->add_eline(atoi(params[4].c_str()), params[2].c_str(), params[5].c_str(), params[1].c_str()); + Instance->XLines->eline_set_creation_time(params[1].c_str(), atoi(params[3].c_str())); + break; + case 'G': + propogate = Instance->XLines->add_gline(atoi(params[4].c_str()), params[2].c_str(), params[5].c_str(), params[1].c_str()); + Instance->XLines->gline_set_creation_time(params[1].c_str(), atoi(params[3].c_str())); + if (propogate) + Utils->lines_to_apply |= APPLY_GLINES; + break; + case 'K': + propogate = Instance->XLines->add_kline(atoi(params[4].c_str()), params[2].c_str(), params[5].c_str(), params[1].c_str()); + if (propogate) + Utils->lines_to_apply |= APPLY_KLINES; + break; + default: + /* Just in case... */ + this->Instance->SNO->WriteToSnoMask('x',"\2WARNING\2: Invalid xline type '"+params[0]+"' sent by server "+prefix+", ignored!"); + propogate = false; + break; + } + /* Send it on its way */ + if (propogate) + { + if (atoi(params[4].c_str())) + { + time_t c_requires_crap = ConvToInt(params[4]) + Instance->Time(); + this->Instance->SNO->WriteToSnoMask('x',"%s Added %cLINE on %s to expire on %s (%s).",prefix.c_str(),*(params[0].c_str()),params[1].c_str(),Instance->TimeString(c_requires_crap).c_str(),params[5].c_str()); + } + else + { + this->Instance->SNO->WriteToSnoMask('x',"%s Added permenant %cLINE on %s (%s).",prefix.c_str(),*(params[0].c_str()),params[1].c_str(),params[5].c_str()); + } + params[5] = ":" + params[5]; + Utils->DoOneToAllButSender(prefix,"ADDLINE",params,prefix); + } + if (!this->bursting) + { + Instance->XLines->apply_lines(Utils->lines_to_apply); + Utils->lines_to_apply = 0; + } + return true; +} + +bool TreeSocket::ChangeName(const std::string &prefix, std::deque<std::string> ¶ms) +{ + if (params.size() < 1) + return true; + userrec* u = this->Instance->FindNick(prefix); + if (u) + { + u->ChangeName(params[0].c_str()); + params[0] = ":" + params[0]; + Utils->DoOneToAllButSender(prefix,"FNAME",params,u->server); + } + return true; +} + +bool TreeSocket::Whois(const std::string &prefix, std::deque<std::string> ¶ms) +{ + if (params.size() < 1) + return true; + userrec* u = this->Instance->FindNick(prefix); + if (u) + { + // an incoming request + if (params.size() == 1) + { + userrec* x = this->Instance->FindNick(params[0]); + if ((x) && (IS_LOCAL(x))) + { + userrec* x = this->Instance->FindNick(params[0]); + char signon[MAXBUF]; + char idle[MAXBUF]; + snprintf(signon, MAXBUF, "%lu", (unsigned long)x->signon); + snprintf(idle, MAXBUF, "%lu", (unsigned long)abs((x->idle_lastmsg) - Instance->Time(true))); + std::deque<std::string> par; + par.push_back(prefix); + par.push_back(signon); + par.push_back(idle); + // ours, we're done, pass it BACK + Utils->DoOneToOne(params[0], "IDLE", par, u->server); + } + else + { + // not ours pass it on + if (x) + Utils->DoOneToOne(prefix, "IDLE", params, x->server); + } + } + else if (params.size() == 3) + { + std::string who_did_the_whois = params[0]; + userrec* who_to_send_to = this->Instance->FindNick(who_did_the_whois); + if ((who_to_send_to) && (IS_LOCAL(who_to_send_to))) + { + // an incoming reply to a whois we sent out + std::string nick_whoised = prefix; + unsigned long signon = atoi(params[1].c_str()); + unsigned long idle = atoi(params[2].c_str()); + if ((who_to_send_to) && (IS_LOCAL(who_to_send_to))) + { + do_whois(this->Instance, who_to_send_to, u, signon, idle, nick_whoised.c_str()); + } + } + else + { + // not ours, pass it on + if (who_to_send_to) + Utils->DoOneToOne(prefix, "IDLE", params, who_to_send_to->server); + } + } + } + return true; +} + +bool TreeSocket::Push(const std::string &prefix, std::deque<std::string> ¶ms) +{ + if (params.size() < 2) + return true; + userrec* u = this->Instance->FindNick(params[0]); + if (!u) + return true; + if (IS_LOCAL(u)) + { + u->Write(params[1]); + } + else + { + // continue the raw onwards + params[1] = ":" + params[1]; + Utils->DoOneToOne(prefix,"PUSH",params,u->server); + } + return true; +} + +bool TreeSocket::HandleSetTime(const std::string &prefix, std::deque<std::string> ¶ms) +{ + if (!params.size() || !Utils->EnableTimeSync) + return true; + + bool force = false; + + if ((params.size() == 2) && (params[1] == "FORCE")) + force = true; + + time_t them = atoi(params[0].c_str()); + time_t us = Instance->Time(false); + + time_t diff = them - us; + + Utils->DoOneToAllButSender(prefix, "TIMESET", params, prefix); + + if (force || (them != us)) + { + time_t old = Instance->SetTimeDelta(diff); + Instance->Log(DEBUG, "TS (diff %d) from %s applied (old delta was %d)", diff, prefix.c_str(), old); + } + + return true; +} + +bool TreeSocket::Time(const std::string &prefix, std::deque<std::string> ¶ms) +{ + // :source.server TIME remote.server sendernick + // :remote.server TIME source.server sendernick TS + if (params.size() == 2) + { + // someone querying our time? + if (this->Instance->Config->ServerName == params[0]) + { + userrec* u = this->Instance->FindNick(params[1]); + if (u) + { + params.push_back(ConvToStr(Instance->Time(false))); + params[0] = prefix; + Utils->DoOneToOne(this->Instance->Config->ServerName,"TIME",params,params[0]); + } + } + else + { + // not us, pass it on + userrec* u = this->Instance->FindNick(params[1]); + if (u) + Utils->DoOneToOne(prefix,"TIME",params,params[0]); + } + } + else if (params.size() == 3) + { + // a response to a previous TIME + userrec* u = this->Instance->FindNick(params[1]); + if ((u) && (IS_LOCAL(u))) + { + time_t rawtime = atol(params[2].c_str()); + struct tm * timeinfo; + timeinfo = localtime(&rawtime); + char tms[26]; + snprintf(tms,26,"%s",asctime(timeinfo)); + tms[24] = 0; + u->WriteServ("391 %s %s :%s",u->nick,prefix.c_str(),tms); + } + else + { + if (u) + Utils->DoOneToOne(prefix,"TIME",params,u->server); + } + } + return true; +} + +bool TreeSocket::LocalPing(const std::string &prefix, std::deque<std::string> ¶ms) +{ + if (params.size() < 1) + return true; + if (params.size() == 1) + { + std::string stufftobounce = params[0]; + this->WriteLine(std::string(":")+this->Instance->Config->ServerName+" PONG "+stufftobounce); + return true; + } + else + { + std::string forwardto = params[1]; + if (forwardto == this->Instance->Config->ServerName) + { + // this is a ping for us, send back PONG to the requesting server + params[1] = params[0]; + params[0] = forwardto; + Utils->DoOneToOne(forwardto,"PONG",params,params[1]); + } + else + { + // not for us, pass it on :) + Utils->DoOneToOne(prefix,"PING",params,forwardto); + } + return true; + } +} + +/** TODO: This creates a total mess of output and needs to really use irc::modestacker. + */ +bool TreeSocket::RemoveStatus(const std::string &prefix, std::deque<std::string> ¶ms) +{ + if (params.size() < 1) + return true; + chanrec* c = Instance->FindChan(params[0]); + if (c) + { + for (char modeletter = 'A'; modeletter <= 'z'; modeletter++) + { + ModeHandler* mh = Instance->Modes->FindMode(modeletter, MODETYPE_CHANNEL); + if (mh) + mh->RemoveMode(c); + } + } + return true; +} + +bool TreeSocket::RemoteServer(const std::string &prefix, std::deque<std::string> ¶ms) +{ + if (params.size() < 4) + return false; + std::string servername = params[0]; + std::string password = params[1]; + // hopcount is not used for a remote server, we calculate this ourselves + std::string description = params[3]; + TreeServer* ParentOfThis = Utils->FindServer(prefix); + if (!ParentOfThis) + { + this->SendError("Protocol error - Introduced remote server from unknown server "+prefix); + return false; + } + TreeServer* CheckDupe = Utils->FindServer(servername); + if (CheckDupe) + { + this->SendError("Server "+servername+" already exists!"); + this->Instance->SNO->WriteToSnoMask('l',"Server \2"+servername+"\2 being introduced from \2" + prefix + "\2 denied, already exists. Closing link with " + prefix); + return false; + } + Link* lnk = Utils->FindLink(servername); + TreeServer* Node = new TreeServer(this->Utils,this->Instance,servername,description,ParentOfThis,NULL, lnk ? lnk->Hidden : false); + ParentOfThis->AddChild(Node); + params[3] = ":" + params[3]; + Utils->SetRemoteBursting(Node, true); + Utils->DoOneToAllButSender(prefix,"SERVER",params,prefix); + this->Instance->SNO->WriteToSnoMask('l',"Server \002"+prefix+"\002 introduced server \002"+servername+"\002 ("+description+")"); + return true; +} + +bool TreeSocket::ComparePass(const std::string &ours, const std::string &theirs) +{ + if ((!strncmp(ours.c_str(), "HMAC-SHA256:", 12)) || (!strncmp(theirs.c_str(), "HMAC-SHA256:", 12))) + { + /* One or both of us specified hmac sha256, but we don't have sha256 module loaded! + * We can't allow this password as valid. + */ + if (!Instance->FindModule("m_sha256.so") || !Utils->ChallengeResponse) + return false; + else + /* Straight string compare of hashes */ + return ours == theirs; + } + else + /* Straight string compare of plaintext */ + return ours == theirs; +} + +bool TreeSocket::Outbound_Reply_Server(std::deque<std::string> ¶ms) +{ + if (params.size() < 4) + return false; + + irc::string servername = params[0].c_str(); + std::string sname = params[0]; + std::string password = params[1]; + std::string description = params[3]; + int hops = atoi(params[2].c_str()); + + this->InboundServerName = sname; + this->InboundDescription = description; + + if (!sentcapab) + this->SendCapabilities(); + + if (hops) + { + this->SendError("Server too far away for authentication"); + this->Instance->SNO->WriteToSnoMask('l',"Server connection from \2"+sname+"\2 denied, server is too far away for authentication"); + return false; + } + + for (std::vector<Link>::iterator x = Utils->LinkBlocks.begin(); x < Utils->LinkBlocks.end(); x++) + { + if ((x->Name == servername) && ((ComparePass(this->MakePass(x->RecvPass,this->GetOurChallenge()),password)) || (x->RecvPass == password && (this->GetTheirChallenge().empty())))) + { + TreeServer* CheckDupe = Utils->FindServer(sname); + if (CheckDupe) + { + this->SendError("Server "+sname+" already exists on server "+CheckDupe->GetParent()->GetName()+"!"); + this->Instance->SNO->WriteToSnoMask('l',"Server connection from \2"+sname+"\2 denied, already exists on server "+CheckDupe->GetParent()->GetName()); + return false; + } + // Begin the sync here. this kickstarts the + // other side, waiting in WAIT_AUTH_2 state, + // into starting their burst, as it shows + // that we're happy. + this->LinkState = CONNECTED; + // we should add the details of this server now + // to the servers tree, as a child of the root + // node. + TreeServer* Node = new TreeServer(this->Utils,this->Instance,sname,description,Utils->TreeRoot,this,x->Hidden); + Utils->TreeRoot->AddChild(Node); + params[3] = ":" + params[3]; + Utils->DoOneToAllButSender(Utils->TreeRoot->GetName(),"SERVER",params,sname); + this->bursting = true; + this->DoBurst(Node); + return true; + } + } + this->SendError("Invalid credentials"); + this->Instance->SNO->WriteToSnoMask('l',"Server connection from \2"+sname+"\2 denied, invalid link credentials"); + return false; +} + +bool TreeSocket::Inbound_Server(std::deque<std::string> ¶ms) +{ + if (params.size() < 4) + return false; + irc::string servername = params[0].c_str(); + std::string sname = params[0]; + std::string password = params[1]; + std::string description = params[3]; + int hops = atoi(params[2].c_str()); + + this->InboundServerName = sname; + this->InboundDescription = description; + + if (!sentcapab) + this->SendCapabilities(); + + if (hops) + { + this->SendError("Server too far away for authentication"); + this->Instance->SNO->WriteToSnoMask('l',"Server connection from \2"+sname+"\2 denied, server is too far away for authentication"); + return false; + } + + for (std::vector<Link>::iterator x = Utils->LinkBlocks.begin(); x < Utils->LinkBlocks.end(); x++) + { + if ((x->Name == servername) && ((ComparePass(this->MakePass(x->RecvPass,this->GetOurChallenge()),password) || x->RecvPass == password && (this->GetTheirChallenge().empty())))) + { + /* First check for instances of the server that are waiting between the inbound and outbound SERVER command */ + TreeSocket* CheckDupeSocket = Utils->FindBurstingServer(sname); + if (CheckDupeSocket) + { + /* If we find one, we abort the link to prevent a race condition */ + this->SendError("Negotiation collision"); + this->Instance->SNO->WriteToSnoMask('l',"Server connection from \2"+sname+"\2 denied, already exists in a negotiating state."); + CheckDupeSocket->SendError("Negotiation collision"); + Instance->SE->DelFd(CheckDupeSocket); + CheckDupeSocket->Close(); + delete CheckDupeSocket; + return false; + } + /* Now check for fully initialized instances of the server */ + TreeServer* CheckDupe = Utils->FindServer(sname); + if (CheckDupe) + { + this->SendError("Server "+sname+" already exists on server "+CheckDupe->GetParent()->GetName()+"!"); + this->Instance->SNO->WriteToSnoMask('l',"Server connection from \2"+sname+"\2 denied, already exists on server "+CheckDupe->GetParent()->GetName()); + return false; + } + this->Instance->SNO->WriteToSnoMask('l',"Verified incoming server connection from \002"+sname+"\002["+(x->HiddenFromStats ? "<hidden>" : this->GetIP())+"] ("+description+")"); + if (this->Hook) + { + std::string name = InspSocketNameRequest((Module*)Utils->Creator, this->Hook).Send(); + this->Instance->SNO->WriteToSnoMask('l',"Connection from \2"+sname+"\2["+(x->HiddenFromStats ? "<hidden>" : this->GetIP())+"] using transport \2"+name+"\2"); + } + + Utils->AddBurstingServer(sname,this); + + // this is good. Send our details: Our server name and description and hopcount of 0, + // along with the sendpass from this block. + this->WriteLine(std::string("SERVER ")+this->Instance->Config->ServerName+" "+this->MakePass(x->SendPass, this->GetTheirChallenge())+" 0 :"+this->Instance->Config->ServerDesc); + // move to the next state, we are now waiting for THEM. + this->LinkState = WAIT_AUTH_2; + return true; + } + } + this->SendError("Invalid credentials"); + this->Instance->SNO->WriteToSnoMask('l',"Server connection from \2"+sname+"\2 denied, invalid link credentials"); + return false; +} + +void TreeSocket::Split(const std::string &line, std::deque<std::string> &n) +{ + n.clear(); + irc::tokenstream tokens(line); + std::string param; + while (tokens.GetToken(param)) + { + if (!param.empty()) + n.push_back(param); + } + return; +} + +bool TreeSocket::ProcessLine(std::string &line) +{ + std::deque<std::string> params; + irc::string command; + std::string prefix; + + line = line.substr(0, line.find_first_of("\r\n")); + + if (line.empty()) + return true; + + Instance->Log(DEBUG, "S[%d] <- %s", this->GetFd(), line.c_str()); + + this->Split(line.c_str(),params); + + if (params.empty()) + return true; + + if ((params[0][0] == ':') && (params.size() > 1)) + { + prefix = params[0].substr(1); + params.pop_front(); + } + command = params[0].c_str(); + params.pop_front(); + switch (this->LinkState) + { + TreeServer* Node; + + case WAIT_AUTH_1: + // Waiting for SERVER command from remote server. Server initiating + // the connection sends the first SERVER command, listening server + // replies with theirs if its happy, then if the initiator is happy, + // it starts to send its net sync, which starts the merge, otherwise + // it sends an ERROR. + if (command == "PASS") + { + /* Silently ignored */ + } + else if (command == "SERVER") + { + return this->Inbound_Server(params); + } + else if (command == "ERROR") + { + return this->Error(params); + } + else if (command == "USER") + { + this->SendError("Client connections to this port are prohibited."); + return false; + } + else if (command == "CAPAB") + { + return this->Capab(params); + } + else if ((command == "U") || (command == "S")) + { + this->SendError("Cannot use the old-style mesh linking protocol with m_spanningtree.so!"); + return false; + } + else + { + irc::string error = "Invalid command in negotiation phase: " + command; + this->SendError(assign(error)); + return false; + } + break; + case WAIT_AUTH_2: + // Waiting for start of other side's netmerge to say they liked our + // password. + if (command == "SERVER") + { + // cant do this, they sent it to us in the WAIT_AUTH_1 state! + // silently ignore. + return true; + } + else if ((command == "U") || (command == "S")) + { + this->SendError("Cannot use the old-style mesh linking protocol with m_spanningtree.so!"); + return false; + } + else if (command == "BURST") + { + if (params.size() && Utils->EnableTimeSync) + { + bool we_have_delta = (Instance->Time(false) != Instance->Time(true)); + time_t them = atoi(params[0].c_str()); + time_t delta = them - Instance->Time(false); + if ((delta < -300) || (delta > 300)) + { + Instance->SNO->WriteToSnoMask('l',"\2ERROR\2: Your clocks are out by %d seconds (this is more than five minutes). Link aborted, \2PLEASE SYNC YOUR CLOCKS!\2",abs(delta)); + SendError("Your clocks are out by "+ConvToStr(abs(delta))+" seconds (this is more than five minutes). Link aborted, PLEASE SYNC YOUR CLOCKS!"); + return false; + } + else if ((delta < -30) || (delta > 30)) + { + Instance->SNO->WriteToSnoMask('l',"\2WARNING\2: Your clocks are out by %d seconds. Please consider synching your clocks.", abs(delta)); + } + + if (!Utils->MasterTime && !we_have_delta) + { + this->Instance->SetTimeDelta(delta); + // Send this new timestamp to any other servers + Utils->DoOneToMany(Utils->TreeRoot->GetName(), "TIMESET", params); + } + } + this->LinkState = CONNECTED; + Link* lnk = Utils->FindLink(InboundServerName); + Node = new TreeServer(this->Utils,this->Instance, InboundServerName, InboundDescription, Utils->TreeRoot, this, lnk ? lnk->Hidden : false); + Utils->DelBurstingServer(this); + Utils->TreeRoot->AddChild(Node); + params.clear(); + params.push_back(InboundServerName); + params.push_back("*"); + params.push_back("1"); + params.push_back(":"+InboundDescription); + Utils->DoOneToAllButSender(Utils->TreeRoot->GetName(),"SERVER",params,InboundServerName); + this->bursting = true; + this->DoBurst(Node); + } + else if (command == "ERROR") + { + return this->Error(params); + } + else if (command == "CAPAB") + { + return this->Capab(params); + } + + break; + case LISTENER: + this->SendError("Internal error -- listening socket accepted its own descriptor!!!"); + return false; + break; + case CONNECTING: + if (command == "SERVER") + { + // another server we connected to, which was in WAIT_AUTH_1 state, + // has just sent us their credentials. If we get this far, theyre + // happy with OUR credentials, and they are now in WAIT_AUTH_2 state. + // if we're happy with this, we should send our netburst which + // kickstarts the merge. + return this->Outbound_Reply_Server(params); + } + else if (command == "ERROR") + { + return this->Error(params); + } + else if (command == "CAPAB") + { + return this->Capab(params); + } + break; + case CONNECTED: + // This is the 'authenticated' state, when all passwords + // have been exchanged and anything past this point is taken + // as gospel. + + if (!prefix.empty()) + { + std::string direction = prefix; + userrec* t = this->Instance->FindNick(prefix); + if (t) + { + direction = t->server; + } + TreeServer* route_back_again = Utils->BestRouteTo(direction); + if ((!route_back_again) || (route_back_again->GetSocket() != this)) + { + if (route_back_again) + Instance->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) + 60); + route_back_again->SetPingFlag(); + } + else + { + prefix = this->GetName(); + } + + if ((command == "MODE") && (params.size() >= 2)) + { + chanrec* channel = Instance->FindChan(params[0]); + if (channel) + { + userrec* x = Instance->FindNick(prefix); + if (x) + { + if (warned.find(x->server) == warned.end()) + { + Instance->Log(DEFAULT,"WARNING: I revceived modes '%s' from another server '%s'. This is not compliant with InspIRCd. Please check that server for bugs.", params[1].c_str(), x->server); + Instance->SNO->WriteToSnoMask('d', "WARNING: The server %s is sending nonstandard modes: '%s MODE %s' where FMODE should be used, and may cause desyncs.", x->server, x->nick, params[1].c_str()); + warned[x->server] = x->nick; + } + } + } + } + + if (command == "SVSMODE") + { + /* Services expects us to implement + * SVSMODE. In inspircd its the same as + * MODE anyway. + */ + command = "MODE"; + } + std::string target; + /* Yes, know, this is a mess. Its reasonably fast though as we're + * working with std::string here. + */ + if ((command == "NICK") && (params.size() >= 8)) + { + return this->IntroduceClient(prefix,params); + } + else if (command == "FJOIN") + { + TreeServer* ServerSource = Utils->FindServer(prefix); + if (ServerSource) + Utils->SetRemoteBursting(ServerSource, false); + return this->ForceJoin(prefix,params); + } + else if (command == "STATS") + { + return this->Stats(prefix, params); + } + else if (command == "MOTD") + { + return this->Motd(prefix, params); + } + else if (command == "KILL" && Utils->IsServer(prefix)) + { + return this->RemoteKill(prefix,params); + } + else if (command == "MODULES") + { + return this->Modules(prefix, params); + } + else if (command == "ADMIN") + { + return this->Admin(prefix, params); + } + else if (command == "SERVER") + { + return this->RemoteServer(prefix,params); + } + else if (command == "ERROR") + { + return this->Error(params); + } + else if (command == "OPERTYPE") + { + return this->OperType(prefix,params); + } + else if (command == "FMODE") + { + TreeServer* ServerSource = Utils->FindServer(prefix); + if (ServerSource) + Utils->SetRemoteBursting(ServerSource, false); + return this->ForceMode(prefix,params); + } + else if (command == "FTOPIC") + { + return this->ForceTopic(prefix,params); + } + else if (command == "REHASH") + { + return this->RemoteRehash(prefix,params); + } + else if (command == "METADATA") + { + return this->MetaData(prefix,params); + } + else if (command == "REMSTATUS") + { + return this->RemoveStatus(prefix,params); + } + else if (command == "PING") + { + if (prefix.empty()) + prefix = this->GetName(); + /* + * We just got a ping from a server that's bursting. + * This can't be right, so set them to not bursting, and + * apply their lines. + */ + TreeServer* ServerSource = Utils->FindServer(prefix); + if (ServerSource) + Utils->SetRemoteBursting(ServerSource, false); + + if (this->bursting) + { + this->bursting = false; + Instance->XLines->apply_lines(Utils->lines_to_apply); + Utils->lines_to_apply = 0; + } + + return this->LocalPing(prefix,params); + } + else if (command == "PONG") + { + if (prefix.empty()) + prefix = this->GetName(); + /* + * We just got a pong from a server that's bursting. + * This can't be right, so set them to not bursting, and + * apply their lines. + */ + TreeServer* ServerSource = Utils->FindServer(prefix); + if (ServerSource) + Utils->SetRemoteBursting(ServerSource, false); + + if (this->bursting) + { + this->bursting = false; + Instance->XLines->apply_lines(Utils->lines_to_apply); + Utils->lines_to_apply = 0; + } + + return this->LocalPong(prefix,params); + } + else if (command == "VERSION") + { + return this->ServerVersion(prefix,params); + } + else if (command == "FHOST") + { + return this->ChangeHost(prefix,params); + } + else if (command == "FNAME") + { + return this->ChangeName(prefix,params); + } + else if (command == "ADDLINE") + { + TreeServer* ServerSource = Utils->FindServer(prefix); + if (ServerSource) + Utils->SetRemoteBursting(ServerSource, false); + return this->AddLine(prefix,params); + } + else if (command == "SVSNICK") + { + if (prefix.empty()) + { + prefix = this->GetName(); + } + return this->ForceNick(prefix,params); + } + else if (command == "OPERQUIT") + { + return this->OperQuit(prefix,params); + } + else if (command == "IDLE") + { + return this->Whois(prefix,params); + } + else if (command == "PUSH") + { + return this->Push(prefix,params); + } + else if (command == "TIMESET") + { + return this->HandleSetTime(prefix, params); + } + else if (command == "TIME") + { + return this->Time(prefix,params); + } + else if ((command == "KICK") && (Utils->IsServer(prefix))) + { + std::string sourceserv = this->myhost; + if (params.size() == 3) + { + userrec* user = this->Instance->FindNick(params[1]); + chanrec* chan = this->Instance->FindChan(params[0]); + if (user && chan) + { + if (!chan->ServerKickUser(user, params[2].c_str(), false)) + /* Yikes, the channels gone! */ + delete chan; + } + } + if (!this->InboundServerName.empty()) + { + sourceserv = this->InboundServerName; + } + return Utils->DoOneToAllButSenderRaw(line,sourceserv,prefix,command,params); + } + else if (command == "SVSJOIN") + { + if (prefix.empty()) + { + prefix = this->GetName(); + } + return this->ServiceJoin(prefix,params); + } + else if (command == "SQUIT") + { + if (params.size() == 2) + { + this->Squit(Utils->FindServer(params[0]),params[1]); + } + return true; + } + else if (command == "OPERNOTICE") + { + std::string sourceserv = this->myhost; + if (!this->InboundServerName.empty()) + sourceserv = this->InboundServerName; + if (params.size() >= 1) + Instance->WriteOpers("*** From " + sourceserv + ": " + params[0]); + return Utils->DoOneToAllButSenderRaw(line, sourceserv, prefix, command, params); + } + else if (command == "MODENOTICE") + { + std::string sourceserv = this->myhost; + if (!this->InboundServerName.empty()) + sourceserv = this->InboundServerName; + if (params.size() >= 2) + { + Instance->WriteMode(params[0].c_str(), WM_AND, "*** From %s: %s", sourceserv.c_str(), params[1].c_str()); + } + return Utils->DoOneToAllButSenderRaw(line, sourceserv, prefix, command, params); + } + else if (command == "SNONOTICE") + { + std::string sourceserv = this->myhost; + if (!this->InboundServerName.empty()) + sourceserv = this->InboundServerName; + if (params.size() >= 2) + { + Instance->SNO->WriteToSnoMask(*(params[0].c_str()), "From " + sourceserv + ": "+ params[1]); + } + return Utils->DoOneToAllButSenderRaw(line, sourceserv, prefix, command, params); + } + else if (command == "ENDBURST") + { + this->bursting = false; + Instance->XLines->apply_lines(Utils->lines_to_apply); + Utils->lines_to_apply = 0; + std::string sourceserv = this->myhost; + if (!this->InboundServerName.empty()) + sourceserv = this->InboundServerName; + this->Instance->SNO->WriteToSnoMask('l',"Received end of netburst from \2%s\2",sourceserv.c_str()); + + Event rmode((char*)sourceserv.c_str(), (Module*)Utils->Creator, "new_server"); + rmode.Send(Instance); + + return true; + } + else + { + // not a special inter-server command. + // Emulate the actual user doing the command, + // this saves us having a huge ugly parser. + userrec* who = this->Instance->FindNick(prefix); + std::string sourceserv = this->myhost; + if (!this->InboundServerName.empty()) + { + sourceserv = this->InboundServerName; + } + if ((!who) && (command == "MODE")) + { + if (Utils->IsServer(prefix)) + { + const char* modelist[127]; + for (size_t i = 0; i < params.size(); i++) + modelist[i] = params[i].c_str(); + userrec* fake = new userrec(Instance); + fake->SetFd(FD_MAGIC_NUMBER); + this->Instance->SendMode(modelist, params.size(), fake); + + delete fake; + /* Hot potato! pass it on! */ + return Utils->DoOneToAllButSenderRaw(line,sourceserv,prefix,command,params); + } + } + if (who) + { + if ((command == "NICK") && (params.size() > 0)) + { + /* On nick messages, check that the nick doesnt + * already exist here. If it does, kill their copy, + * and our copy. + */ + userrec* x = this->Instance->FindNick(params[0]); + if ((x) && (x != who)) + { + std::deque<std::string> p; + p.push_back(params[0]); + p.push_back("Nickname collision ("+prefix+" -> "+params[0]+")"); + Utils->DoOneToMany(this->Instance->Config->ServerName,"KILL",p); + p.clear(); + p.push_back(prefix); + p.push_back("Nickname collision"); + Utils->DoOneToMany(this->Instance->Config->ServerName,"KILL",p); + userrec::QuitUser(this->Instance,x,"Nickname collision ("+prefix+" -> "+params[0]+")"); + userrec* y = this->Instance->FindNick(prefix); + if (y) + { + userrec::QuitUser(this->Instance,y,"Nickname collision"); + } + return Utils->DoOneToAllButSenderRaw(line,sourceserv,prefix,command,params); + } + } + // its a user + target = who->server; + const char* strparams[127]; + for (unsigned int q = 0; q < params.size(); q++) + { + strparams[q] = params[q].c_str(); + } + switch (this->Instance->CallCommandHandler(command.c_str(), strparams, params.size(), who)) + { + case CMD_INVALID: + this->SendError("Unrecognised command '"+std::string(command.c_str())+"' -- possibly loaded mismatched modules"); + return false; + break; + case CMD_FAILURE: + return true; + break; + default: + /* CMD_SUCCESS and CMD_USER_DELETED fall through here */ + break; + } + } + else + { + // its not a user. Its either a server, or somethings screwed up. + if (Utils->IsServer(prefix)) + target = this->Instance->Config->ServerName; + else + return true; + } + return Utils->DoOneToAllButSenderRaw(line,sourceserv,prefix,command,params); + + } + return true; + break; + } + return true; +} + +std::string TreeSocket::GetName() +{ + std::string sourceserv = this->myhost; + if (!this->InboundServerName.empty()) + { + sourceserv = this->InboundServerName; + } + return sourceserv; +} + +void TreeSocket::OnTimeout() +{ + if (this->LinkState == CONNECTING) + { + this->Instance->SNO->WriteToSnoMask('l',"CONNECT: Connection to \002"+myhost+"\002 timed out."); + Link* MyLink = Utils->FindLink(myhost); + if (MyLink) + Utils->DoFailOver(MyLink); + } +} + +void TreeSocket::OnClose() +{ + // Connection closed. + // If the connection is fully up (state CONNECTED) + // then propogate a netsplit to all peers. + std::string quitserver = this->myhost; + if (!this->InboundServerName.empty()) + { + quitserver = this->InboundServerName; + } + TreeServer* s = Utils->FindServer(quitserver); + if (s) + { + Squit(s,"Remote host closed the connection"); + } + + if (!quitserver.empty()) + { + this->Instance->SNO->WriteToSnoMask('l',"Connection to '\2%s\2' failed.",quitserver.c_str()); + time_t server_uptime = Instance->Time() - this->age; + if (server_uptime) + Instance->SNO->WriteToSnoMask('l',"Connection to '\2%s\2' was established for %s", quitserver.c_str(), Utils->Creator->TimeToStr(server_uptime).c_str()); + } +} + +int TreeSocket::OnIncomingConnection(int newsock, char* ip) +{ + /* To prevent anyone from attempting to flood opers/DDoS by connecting to the server port, + * or discovering if this port is the server port, we don't allow connections from any + * IPs for which we don't have a link block. + */ + bool found = false; + + found = (std::find(Utils->ValidIPs.begin(), Utils->ValidIPs.end(), ip) != Utils->ValidIPs.end()); + if (!found) + { + for (vector<std::string>::iterator i = Utils->ValidIPs.begin(); i != Utils->ValidIPs.end(); i++) + if (irc::sockets::MatchCIDR(ip, (*i).c_str())) + found = true; + + if (!found) + { + this->Instance->SNO->WriteToSnoMask('l',"Server connection from %s denied (no link blocks with that IP address)", ip); + close(newsock); + return false; + } + } + + TreeSocket* s = new TreeSocket(this->Utils, this->Instance, newsock, ip, this->Hook); + s = s; /* Whinge whinge whinge, thats all GCC ever does. */ + return true; +} diff --git a/src/modules/m_spanningtree/utils.cpp b/src/modules/m_spanningtree/utils.cpp index 4d0256fa2..9675a6ac8 100644 --- a/src/modules/m_spanningtree/utils.cpp +++ b/src/modules/m_spanningtree/utils.cpp @@ -1 +1,649 @@ -/* +------------------------------------+
* | 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 "users.h"
#include "channels.h"
#include "modules.h"
#include "commands/cmd_whois.h"
#include "commands/cmd_stats.h"
#include "socket.h"
#include "wildcard.h"
#include "xline.h"
#include "transport.h"
#include "socketengine.h"
#include "m_spanningtree/main.h"
#include "m_spanningtree/utils.h"
#include "m_spanningtree/treeserver.h"
#include "m_spanningtree/link.h"
#include "m_spanningtree/treesocket.h"
#include "m_spanningtree/resolvers.h"
/* $ModDep: m_spanningtree/timesynctimer.h m_spanningtree/resolvers.h m_spanningtree/main.h m_spanningtree/utils.h m_spanningtree/treeserver.h m_spanningtree/link.h m_spanningtree/treesocket.h */
/** Yay for fast searches!
* This is hundreds of times faster than recursion
* or even scanning a linked list, especially when
* there are more than a few servers to deal with.
* (read as: lots).
*/
TreeServer* SpanningTreeUtilities::FindServer(const std::string &ServerName)
{
server_hash::iterator iter = serverlist.find(ServerName.c_str());
if (iter != serverlist.end())
{
return iter->second;
}
else
{
return NULL;
}
}
TreeServer* SpanningTreeUtilities::FindRemoteBurstServer(TreeServer* Server)
{
server_hash::iterator iter = RemoteServersBursting.find(Server->GetName().c_str());
if (iter != RemoteServersBursting.end())
return iter->second;
else
return NULL;
}
TreeSocket* SpanningTreeUtilities::FindBurstingServer(const std::string &ServerName)
{
std::map<irc::string,TreeSocket*>::iterator iter;
iter = burstingserverlist.find(ServerName.c_str());
if (iter != burstingserverlist.end())
{
return iter->second;
}
else
{
return NULL;
}
}
void SpanningTreeUtilities::SetRemoteBursting(TreeServer* Server, bool bursting)
{
server_hash::iterator iter = RemoteServersBursting.find(Server->GetName().c_str());
if (bursting)
{
if (iter == RemoteServersBursting.end())
RemoteServersBursting.insert(make_pair(Server->GetName(), Server));
else return;
}
else
{
if (iter != RemoteServersBursting.end())
RemoteServersBursting.erase(iter);
else return;
}
ServerInstance->Log(DEBUG,"Server %s is %sbursting nicknames", Server->GetName().c_str(), bursting ? "" : "no longer ");
}
void SpanningTreeUtilities::AddBurstingServer(const std::string &ServerName, TreeSocket* s)
{
std::map<irc::string,TreeSocket*>::iterator iter = burstingserverlist.find(ServerName.c_str());
if (iter == burstingserverlist.end())
burstingserverlist[ServerName.c_str()] = s;
}
void SpanningTreeUtilities::DelBurstingServer(TreeSocket* s)
{
for (std::map<irc::string,TreeSocket*>::iterator iter = burstingserverlist.begin(); iter != burstingserverlist.end(); iter++)
{
if (iter->second == s)
{
burstingserverlist.erase(iter);
return;
}
}
}
/** Returns the locally connected server we must route a
* message through to reach server 'ServerName'. This
* only applies to one-to-one and not one-to-many routing.
* See the comments for the constructor of TreeServer
* for more details.
*/
TreeServer* SpanningTreeUtilities::BestRouteTo(const std::string &ServerName)
{
if (ServerName.c_str() == TreeRoot->GetName())
return NULL;
TreeServer* Found = FindServer(ServerName);
if (Found)
{
return Found->GetRoute();
}
else
{
return NULL;
}
}
/** Find the first server matching a given glob mask.
* Theres no find-using-glob method of hash_map [awwww :-(]
* so instead, we iterate over the list using an iterator
* and match each one until we get a hit. Yes its slow,
* deal with it.
*/
TreeServer* SpanningTreeUtilities::FindServerMask(const std::string &ServerName)
{
for (server_hash::iterator i = serverlist.begin(); i != serverlist.end(); i++)
{
if (match(i->first.c_str(),ServerName.c_str()))
return i->second;
}
return NULL;
}
/* A convenient wrapper that returns true if a server exists */
bool SpanningTreeUtilities::IsServer(const std::string &ServerName)
{
return (FindServer(ServerName) != NULL);
}
SpanningTreeUtilities::SpanningTreeUtilities(InspIRCd* Instance, ModuleSpanningTree* C) : ServerInstance(Instance), Creator(C)
{
Bindings.clear();
lines_to_apply = 0;
this->TreeRoot = new TreeServer(this, ServerInstance, ServerInstance->Config->ServerName, ServerInstance->Config->ServerDesc);
modulelist* ml = ServerInstance->FindInterface("InspSocketHook");
/* Did we find any modules? */
if (ml)
{
/* Yes, enumerate them all to find out the hook name */
for (modulelist::iterator m = ml->begin(); m != ml->end(); m++)
{
/* Make a request to it for its name, its implementing
* InspSocketHook so we know its safe to do this
*/
std::string name = InspSocketNameRequest((Module*)Creator, *m).Send();
/* Build a map of them */
hooks[name.c_str()] = *m;
hooknames.push_back(name);
}
}
this->ReadConfiguration(true);
}
SpanningTreeUtilities::~SpanningTreeUtilities()
{
for (unsigned int i = 0; i < Bindings.size(); i++)
{
ServerInstance->SE->DelFd(Bindings[i]);
Bindings[i]->Close();
DELETE(Bindings[i]);
}
while (TreeRoot->ChildCount())
{
TreeServer* child_server = TreeRoot->GetChild(0);
if (child_server)
{
TreeSocket* sock = child_server->GetSocket();
ServerInstance->SE->DelFd(sock);
sock->Close();
DELETE(sock);
}
}
delete TreeRoot;
}
void SpanningTreeUtilities::AddThisServer(TreeServer* server, TreeServerList &list)
{
if (list.find(server) == list.end())
list[server] = server;
}
/* returns a list of DIRECT servernames for a specific channel */
void SpanningTreeUtilities::GetListOfServersForChannel(chanrec* c, TreeServerList &list, char status, const CUList &exempt_list)
{
CUList *ulist;
switch (status)
{
case '@':
ulist = c->GetOppedUsers();
break;
case '%':
ulist = c->GetHalfoppedUsers();
break;
case '+':
ulist = c->GetVoicedUsers();
break;
default:
ulist = c->GetUsers();
break;
}
for (CUList::iterator i = ulist->begin(); i != ulist->end(); i++)
{
if ((i->first->GetFd() < 0) && (exempt_list.find(i->first) == exempt_list.end()))
{
TreeServer* best = this->BestRouteTo(i->first->server);
if (best)
AddThisServer(best,list);
}
}
return;
}
bool SpanningTreeUtilities::DoOneToAllButSenderRaw(const std::string &data, const std::string &omit, const std::string &prefix, const irc::string &command, std::deque<std::string> ¶ms)
{
char pfx = 0;
TreeServer* omitroute = this->BestRouteTo(omit);
if ((command == "NOTICE") || (command == "PRIVMSG"))
{
if (params.size() >= 2)
{
/* Prefixes */
if ((*(params[0].c_str()) == '@') || (*(params[0].c_str()) == '%') || (*(params[0].c_str()) == '+'))
{
pfx = params[0][0];
params[0] = params[0].substr(1, params[0].length()-1);
}
if ((*(params[0].c_str()) != '#') && (*(params[0].c_str()) != '$'))
{
// special routing for private messages/notices
userrec* d = ServerInstance->FindNick(params[0]);
if (d)
{
std::deque<std::string> par;
par.push_back(params[0]);
par.push_back(":"+params[1]);
this->DoOneToOne(prefix,command.c_str(),par,d->server);
return true;
}
}
else if (*(params[0].c_str()) == '$')
{
std::deque<std::string> par;
par.push_back(params[0]);
par.push_back(":"+params[1]);
this->DoOneToAllButSender(prefix,command.c_str(),par,omitroute->GetName());
return true;
}
else
{
chanrec* c = ServerInstance->FindChan(params[0]);
userrec* u = ServerInstance->FindNick(prefix);
if (c && u)
{
CUList elist;
TreeServerList list;
FOREACH_MOD(I_OnBuildExemptList, OnBuildExemptList((command == "PRIVMSG" ? MSG_PRIVMSG : MSG_NOTICE), c, u, pfx, elist));
GetListOfServersForChannel(c,list,pfx,elist);
for (TreeServerList::iterator i = list.begin(); i != list.end(); i++)
{
TreeSocket* Sock = i->second->GetSocket();
if ((Sock) && (i->second->GetName() != omit) && (omitroute != i->second))
{
Sock->WriteLine(data);
}
}
return true;
}
}
}
}
unsigned int items =this->TreeRoot->ChildCount();
for (unsigned int x = 0; x < items; x++)
{
TreeServer* Route = this->TreeRoot->GetChild(x);
if ((Route) && (Route->GetSocket()) && (Route->GetName() != omit) && (omitroute != Route))
{
TreeSocket* Sock = Route->GetSocket();
if (Sock)
Sock->WriteLine(data);
}
}
return true;
}
bool SpanningTreeUtilities::DoOneToAllButSender(const std::string &prefix, const std::string &command, std::deque<std::string> ¶ms, std::string omit)
{
TreeServer* omitroute = this->BestRouteTo(omit);
std::string FullLine = ":" + prefix + " " + command;
unsigned int words = params.size();
for (unsigned int x = 0; x < words; x++)
{
FullLine = FullLine + " " + params[x];
}
unsigned int items = this->TreeRoot->ChildCount();
for (unsigned int x = 0; x < items; x++)
{
TreeServer* Route = this->TreeRoot->GetChild(x);
// Send the line IF:
// The route has a socket (its a direct connection)
// The route isnt the one to be omitted
// The route isnt the path to the one to be omitted
if ((Route) && (Route->GetSocket()) && (Route->GetName() != omit) && (omitroute != Route))
{
TreeSocket* Sock = Route->GetSocket();
if (Sock)
Sock->WriteLine(FullLine);
}
}
return true;
}
bool SpanningTreeUtilities::DoOneToMany(const std::string &prefix, const std::string &command, std::deque<std::string> ¶ms)
{
std::string FullLine = ":" + prefix + " " + command;
unsigned int words = params.size();
for (unsigned int x = 0; x < words; x++)
{
FullLine = FullLine + " " + params[x];
}
unsigned int items = this->TreeRoot->ChildCount();
for (unsigned int x = 0; x < items; x++)
{
TreeServer* Route = this->TreeRoot->GetChild(x);
if (Route && Route->GetSocket())
{
TreeSocket* Sock = Route->GetSocket();
if (Sock)
Sock->WriteLine(FullLine);
}
}
return true;
}
bool SpanningTreeUtilities::DoOneToMany(const char* prefix, const char* command, std::deque<std::string> ¶ms)
{
std::string spfx = prefix;
std::string scmd = command;
return this->DoOneToMany(spfx, scmd, params);
}
bool SpanningTreeUtilities::DoOneToAllButSender(const char* prefix, const char* command, std::deque<std::string> ¶ms, std::string omit)
{
std::string spfx = prefix;
std::string scmd = command;
return this->DoOneToAllButSender(spfx, scmd, params, omit);
}
bool SpanningTreeUtilities::DoOneToOne(const std::string &prefix, const std::string &command, std::deque<std::string> ¶ms, std::string target)
{
TreeServer* Route = this->BestRouteTo(target);
if (Route)
{
std::string FullLine = ":" + prefix + " " + command;
unsigned int words = params.size();
for (unsigned int x = 0; x < words; x++)
{
FullLine = FullLine + " " + params[x];
}
if (Route && Route->GetSocket())
{
TreeSocket* Sock = Route->GetSocket();
if (Sock)
Sock->WriteLine(FullLine);
}
return true;
}
else
{
return false;
}
}
void SpanningTreeUtilities::RefreshIPCache()
{
ValidIPs.clear();
for (std::vector<Link>::iterator L = LinkBlocks.begin(); L != LinkBlocks.end(); L++)
{
if ((!L->IPAddr.empty()) && (!L->RecvPass.empty()) && (!L->SendPass.empty()) && (!L->Name.empty()) && (L->Port))
{
ValidIPs.push_back(L->IPAddr);
if (L->AllowMask.length())
ValidIPs.push_back(L->AllowMask);
/* Needs resolving */
bool ipvalid = true;
QueryType start_type = DNS_QUERY_A;
#ifdef IPV6
start_type = DNS_QUERY_AAAA;
if (strchr(L->IPAddr.c_str(),':'))
{
in6_addr n;
if (inet_pton(AF_INET6, L->IPAddr.c_str(), &n) < 1)
ipvalid = false;
}
else
#endif
{
in_addr n;
if (inet_aton(L->IPAddr.c_str(),&n) < 1)
ipvalid = false;
}
if (!ipvalid)
{
try
{
bool cached;
SecurityIPResolver* sr = new SecurityIPResolver((Module*)this->Creator, this, ServerInstance, L->IPAddr, *L, cached, start_type);
ServerInstance->AddResolver(sr, cached);
}
catch (...)
{
}
}
}
}
}
void SpanningTreeUtilities::ReadConfiguration(bool rebind)
{
ConfigReader* Conf = new ConfigReader(ServerInstance);
if (rebind)
{
for (int j = 0; j < Conf->Enumerate("bind"); j++)
{
std::string Type = Conf->ReadValue("bind","type",j);
std::string IP = Conf->ReadValue("bind","address",j);
std::string Port = Conf->ReadValue("bind","port",j);
std::string transport = Conf->ReadValue("bind","transport",j);
if (Type == "servers")
{
irc::portparser portrange(Port, false);
int portno = -1;
while ((portno = portrange.GetToken()))
{
if (IP == "*")
IP.clear();
if ((!transport.empty()) && (hooks.find(transport.c_str()) == hooks.end()))
{
ServerInstance->Log(DEFAULT,"m_spanningtree: WARNING: Can't find transport type '%s' for port %s:%s - maybe you forgot to load it BEFORE m_spanningtree in your config file? - Skipping this port binding", transport.c_str(), IP.c_str(), Port.c_str());
break;
}
TreeSocket* listener = new TreeSocket(this, ServerInstance, IP.c_str(), portno, true, 10, transport.empty() ? NULL : hooks[transport.c_str()]);
if (listener->GetState() == I_LISTENING)
{
ServerInstance->Log(DEFAULT,"m_spanningtree: Binding server port %s:%d successful!", IP.c_str(), portno);
Bindings.push_back(listener);
}
else
{
ServerInstance->Log(DEFAULT,"m_spanningtree: Warning: Failed to bind server port: %s:%d: %s",IP.c_str(), portno, strerror(errno));
listener->Close();
DELETE(listener);
}
}
}
}
}
FlatLinks = Conf->ReadFlag("options","flatlinks",0);
HideULines = Conf->ReadFlag("options","hideulines",0);
AnnounceTSChange = Conf->ReadFlag("options","announcets",0);
EnableTimeSync = Conf->ReadFlag("timesync","enable",0);
MasterTime = Conf->ReadFlag("timesync", "master", 0);
ChallengeResponse = !Conf->ReadFlag("options", "disablehmac", 0);
quiet_bursts = Conf->ReadFlag("options", "quietbursts", 0);
PingWarnTime = Conf->ReadInteger("options", "pingwarning", 0, true);
if (PingWarnTime < 0 || PingWarnTime > 59)
PingWarnTime = 0;
LinkBlocks.clear();
ValidIPs.clear();
for (int j = 0; j < Conf->Enumerate("link"); j++)
{
Link L;
std::string Allow = Conf->ReadValue("link", "allowmask", j);
L.Name = (Conf->ReadValue("link", "name", j)).c_str();
L.AllowMask = Allow;
L.IPAddr = Conf->ReadValue("link", "ipaddr", j);
L.FailOver = Conf->ReadValue("link", "failover", j).c_str();
L.Port = Conf->ReadInteger("link", "port", j, true);
L.SendPass = Conf->ReadValue("link", "sendpass", j);
L.RecvPass = Conf->ReadValue("link", "recvpass", j);
L.AutoConnect = Conf->ReadInteger("link", "autoconnect", j, true);
L.HiddenFromStats = Conf->ReadFlag("link", "statshidden", j);
L.Timeout = Conf->ReadInteger("link", "timeout", j, true);
L.Hook = Conf->ReadValue("link", "transport", j);
L.Bind = Conf->ReadValue("link", "bind", j);
L.Hidden = Conf->ReadFlag("link", "hidden", j);
if ((!L.Hook.empty()) && (hooks.find(L.Hook.c_str()) == hooks.end()))
{
ServerInstance->Log(DEFAULT,"m_spanningtree: WARNING: Can't find transport type '%s' for link '%s' - maybe you forgot to load it BEFORE m_spanningtree in your config file? Skipping <link> tag completely.",
L.Hook.c_str(), L.Name.c_str());
continue;
}
L.NextConnectTime = time(NULL) + L.AutoConnect;
/* Bugfix by brain, do not allow people to enter bad configurations */
if (L.Name != ServerInstance->Config->ServerName)
{
if ((!L.IPAddr.empty()) && (!L.RecvPass.empty()) && (!L.SendPass.empty()) && (!L.Name.empty()) && (L.Port))
{
ValidIPs.push_back(L.IPAddr);
if (Allow.length())
ValidIPs.push_back(Allow);
/* Needs resolving */
bool ipvalid = true;
QueryType start_type = DNS_QUERY_A;
#ifdef IPV6
start_type = DNS_QUERY_AAAA;
if (strchr(L.IPAddr.c_str(),':'))
{
in6_addr n;
if (inet_pton(AF_INET6, L.IPAddr.c_str(), &n) < 1)
ipvalid = false;
}
else
{
in_addr n;
if (inet_aton(L.IPAddr.c_str(),&n) < 1)
ipvalid = false;
}
#else
in_addr n;
if (inet_aton(L.IPAddr.c_str(),&n) < 1)
ipvalid = false;
#endif
if (!ipvalid)
{
try
{
bool cached;
SecurityIPResolver* sr = new SecurityIPResolver((Module*)this->Creator, this, ServerInstance, L.IPAddr, L, cached, start_type);
ServerInstance->AddResolver(sr, cached);
}
catch (...)
{
}
}
LinkBlocks.push_back(L);
}
else
{
if (L.IPAddr.empty())
{
ServerInstance->Log(DEFAULT,"Invalid configuration for server '%s', IP address not defined!",L.Name.c_str());
}
else if (L.RecvPass.empty())
{
ServerInstance->Log(DEFAULT,"Invalid configuration for server '%s', recvpass not defined!",L.Name.c_str());
}
else if (L.SendPass.empty())
{
ServerInstance->Log(DEFAULT,"Invalid configuration for server '%s', sendpass not defined!",L.Name.c_str());
}
else if (L.Name.empty())
{
ServerInstance->Log(DEFAULT,"Invalid configuration, link tag without a name!");
}
else if (!L.Port)
{
ServerInstance->Log(DEFAULT,"Invalid configuration for server '%s', no port specified!",L.Name.c_str());
}
}
}
else
{
ServerInstance->Log(DEFAULT,"Invalid configuration for server '%s', link tag has the same server name as the local server!",L.Name.c_str());
}
}
DELETE(Conf);
}
void SpanningTreeUtilities::DoFailOver(Link* x)
{
if (x->FailOver.length())
{
if (x->FailOver == x->Name)
{
ServerInstance->SNO->WriteToSnoMask('l',"FAILOVER: Some muppet configured the failover for server \002%s\002 to point at itself. Not following it!", x->Name.c_str());
return;
}
Link* TryThisOne = this->FindLink(x->FailOver.c_str());
if (TryThisOne)
{
ServerInstance->SNO->WriteToSnoMask('l',"FAILOVER: Trying failover link for \002%s\002: \002%s\002...", x->Name.c_str(), TryThisOne->Name.c_str());
Creator->ConnectServer(TryThisOne);
}
else
{
ServerInstance->SNO->WriteToSnoMask('l',"FAILOVER: Invalid failover server specified for server \002%s\002, will not follow!", x->Name.c_str());
}
}
}
Link* SpanningTreeUtilities::FindLink(const std::string& name)
{
for (std::vector<Link>::iterator x = LinkBlocks.begin(); x < LinkBlocks.end(); x++)
{
if (ServerInstance->MatchText(x->Name.c_str(), name.c_str()))
{
return &(*x);
}
}
return NULL;
}
\ No newline at end of file +/* +------------------------------------+ + * | 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 "users.h" +#include "channels.h" +#include "modules.h" +#include "commands/cmd_whois.h" +#include "commands/cmd_stats.h" +#include "socket.h" +#include "wildcard.h" +#include "xline.h" +#include "transport.h" +#include "socketengine.h" + +#include "m_spanningtree/main.h" +#include "m_spanningtree/utils.h" +#include "m_spanningtree/treeserver.h" +#include "m_spanningtree/link.h" +#include "m_spanningtree/treesocket.h" +#include "m_spanningtree/resolvers.h" + +/* $ModDep: m_spanningtree/timesynctimer.h m_spanningtree/resolvers.h m_spanningtree/main.h m_spanningtree/utils.h m_spanningtree/treeserver.h m_spanningtree/link.h m_spanningtree/treesocket.h */ + +/** Yay for fast searches! + * This is hundreds of times faster than recursion + * or even scanning a linked list, especially when + * there are more than a few servers to deal with. + * (read as: lots). + */ +TreeServer* SpanningTreeUtilities::FindServer(const std::string &ServerName) +{ + server_hash::iterator iter = serverlist.find(ServerName.c_str()); + if (iter != serverlist.end()) + { + return iter->second; + } + else + { + return NULL; + } +} + +TreeServer* SpanningTreeUtilities::FindRemoteBurstServer(TreeServer* Server) +{ + server_hash::iterator iter = RemoteServersBursting.find(Server->GetName().c_str()); + if (iter != RemoteServersBursting.end()) + return iter->second; + else + return NULL; +} + +TreeSocket* SpanningTreeUtilities::FindBurstingServer(const std::string &ServerName) +{ + std::map<irc::string,TreeSocket*>::iterator iter; + iter = burstingserverlist.find(ServerName.c_str()); + if (iter != burstingserverlist.end()) + { + return iter->second; + } + else + { + return NULL; + } +} + +void SpanningTreeUtilities::SetRemoteBursting(TreeServer* Server, bool bursting) +{ + server_hash::iterator iter = RemoteServersBursting.find(Server->GetName().c_str()); + if (bursting) + { + if (iter == RemoteServersBursting.end()) + RemoteServersBursting.insert(make_pair(Server->GetName(), Server)); + else return; + } + else + { + if (iter != RemoteServersBursting.end()) + RemoteServersBursting.erase(iter); + else return; + } + ServerInstance->Log(DEBUG,"Server %s is %sbursting nicknames", Server->GetName().c_str(), bursting ? "" : "no longer "); +} + +void SpanningTreeUtilities::AddBurstingServer(const std::string &ServerName, TreeSocket* s) +{ + std::map<irc::string,TreeSocket*>::iterator iter = burstingserverlist.find(ServerName.c_str()); + if (iter == burstingserverlist.end()) + burstingserverlist[ServerName.c_str()] = s; +} + +void SpanningTreeUtilities::DelBurstingServer(TreeSocket* s) +{ + for (std::map<irc::string,TreeSocket*>::iterator iter = burstingserverlist.begin(); iter != burstingserverlist.end(); iter++) + { + if (iter->second == s) + { + burstingserverlist.erase(iter); + return; + } + } +} + +/** Returns the locally connected server we must route a + * message through to reach server 'ServerName'. This + * only applies to one-to-one and not one-to-many routing. + * See the comments for the constructor of TreeServer + * for more details. + */ +TreeServer* SpanningTreeUtilities::BestRouteTo(const std::string &ServerName) +{ + if (ServerName.c_str() == TreeRoot->GetName()) + return NULL; + TreeServer* Found = FindServer(ServerName); + if (Found) + { + return Found->GetRoute(); + } + else + { + return NULL; + } +} + +/** Find the first server matching a given glob mask. + * Theres no find-using-glob method of hash_map [awwww :-(] + * so instead, we iterate over the list using an iterator + * and match each one until we get a hit. Yes its slow, + * deal with it. + */ +TreeServer* SpanningTreeUtilities::FindServerMask(const std::string &ServerName) +{ + for (server_hash::iterator i = serverlist.begin(); i != serverlist.end(); i++) + { + if (match(i->first.c_str(),ServerName.c_str())) + return i->second; + } + return NULL; +} + +/* A convenient wrapper that returns true if a server exists */ +bool SpanningTreeUtilities::IsServer(const std::string &ServerName) +{ + return (FindServer(ServerName) != NULL); +} + +SpanningTreeUtilities::SpanningTreeUtilities(InspIRCd* Instance, ModuleSpanningTree* C) : ServerInstance(Instance), Creator(C) +{ + Bindings.clear(); + + lines_to_apply = 0; + + this->TreeRoot = new TreeServer(this, ServerInstance, ServerInstance->Config->ServerName, ServerInstance->Config->ServerDesc); + + modulelist* ml = ServerInstance->FindInterface("InspSocketHook"); + + /* Did we find any modules? */ + if (ml) + { + /* Yes, enumerate them all to find out the hook name */ + for (modulelist::iterator m = ml->begin(); m != ml->end(); m++) + { + /* Make a request to it for its name, its implementing + * InspSocketHook so we know its safe to do this + */ + std::string name = InspSocketNameRequest((Module*)Creator, *m).Send(); + /* Build a map of them */ + hooks[name.c_str()] = *m; + hooknames.push_back(name); + } + } + + this->ReadConfiguration(true); +} + +SpanningTreeUtilities::~SpanningTreeUtilities() +{ + for (unsigned int i = 0; i < Bindings.size(); i++) + { + ServerInstance->SE->DelFd(Bindings[i]); + Bindings[i]->Close(); + DELETE(Bindings[i]); + } + while (TreeRoot->ChildCount()) + { + TreeServer* child_server = TreeRoot->GetChild(0); + if (child_server) + { + TreeSocket* sock = child_server->GetSocket(); + ServerInstance->SE->DelFd(sock); + sock->Close(); + DELETE(sock); + } + } + delete TreeRoot; +} + +void SpanningTreeUtilities::AddThisServer(TreeServer* server, TreeServerList &list) +{ + if (list.find(server) == list.end()) + list[server] = server; +} + +/* returns a list of DIRECT servernames for a specific channel */ +void SpanningTreeUtilities::GetListOfServersForChannel(chanrec* c, TreeServerList &list, char status, const CUList &exempt_list) +{ + CUList *ulist; + switch (status) + { + case '@': + ulist = c->GetOppedUsers(); + break; + case '%': + ulist = c->GetHalfoppedUsers(); + break; + case '+': + ulist = c->GetVoicedUsers(); + break; + default: + ulist = c->GetUsers(); + break; + } + for (CUList::iterator i = ulist->begin(); i != ulist->end(); i++) + { + if ((i->first->GetFd() < 0) && (exempt_list.find(i->first) == exempt_list.end())) + { + TreeServer* best = this->BestRouteTo(i->first->server); + if (best) + AddThisServer(best,list); + } + } + return; +} + +bool SpanningTreeUtilities::DoOneToAllButSenderRaw(const std::string &data, const std::string &omit, const std::string &prefix, const irc::string &command, std::deque<std::string> ¶ms) +{ + char pfx = 0; + TreeServer* omitroute = this->BestRouteTo(omit); + if ((command == "NOTICE") || (command == "PRIVMSG")) + { + if (params.size() >= 2) + { + /* Prefixes */ + if ((*(params[0].c_str()) == '@') || (*(params[0].c_str()) == '%') || (*(params[0].c_str()) == '+')) + { + pfx = params[0][0]; + params[0] = params[0].substr(1, params[0].length()-1); + } + if ((*(params[0].c_str()) != '#') && (*(params[0].c_str()) != '$')) + { + // special routing for private messages/notices + userrec* d = ServerInstance->FindNick(params[0]); + if (d) + { + std::deque<std::string> par; + par.push_back(params[0]); + par.push_back(":"+params[1]); + this->DoOneToOne(prefix,command.c_str(),par,d->server); + return true; + } + } + else if (*(params[0].c_str()) == '$') + { + std::deque<std::string> par; + par.push_back(params[0]); + par.push_back(":"+params[1]); + this->DoOneToAllButSender(prefix,command.c_str(),par,omitroute->GetName()); + return true; + } + else + { + chanrec* c = ServerInstance->FindChan(params[0]); + userrec* u = ServerInstance->FindNick(prefix); + if (c && u) + { + CUList elist; + TreeServerList list; + FOREACH_MOD(I_OnBuildExemptList, OnBuildExemptList((command == "PRIVMSG" ? MSG_PRIVMSG : MSG_NOTICE), c, u, pfx, elist)); + GetListOfServersForChannel(c,list,pfx,elist); + + for (TreeServerList::iterator i = list.begin(); i != list.end(); i++) + { + TreeSocket* Sock = i->second->GetSocket(); + if ((Sock) && (i->second->GetName() != omit) && (omitroute != i->second)) + { + Sock->WriteLine(data); + } + } + return true; + } + } + } + } + unsigned int items =this->TreeRoot->ChildCount(); + for (unsigned int x = 0; x < items; x++) + { + TreeServer* Route = this->TreeRoot->GetChild(x); + if ((Route) && (Route->GetSocket()) && (Route->GetName() != omit) && (omitroute != Route)) + { + TreeSocket* Sock = Route->GetSocket(); + if (Sock) + Sock->WriteLine(data); + } + } + return true; +} + +bool SpanningTreeUtilities::DoOneToAllButSender(const std::string &prefix, const std::string &command, std::deque<std::string> ¶ms, std::string omit) +{ + TreeServer* omitroute = this->BestRouteTo(omit); + std::string FullLine = ":" + prefix + " " + command; + unsigned int words = params.size(); + for (unsigned int x = 0; x < words; x++) + { + FullLine = FullLine + " " + params[x]; + } + unsigned int items = this->TreeRoot->ChildCount(); + for (unsigned int x = 0; x < items; x++) + { + TreeServer* Route = this->TreeRoot->GetChild(x); + // Send the line IF: + // The route has a socket (its a direct connection) + // The route isnt the one to be omitted + // The route isnt the path to the one to be omitted + if ((Route) && (Route->GetSocket()) && (Route->GetName() != omit) && (omitroute != Route)) + { + TreeSocket* Sock = Route->GetSocket(); + if (Sock) + Sock->WriteLine(FullLine); + } + } + return true; +} + +bool SpanningTreeUtilities::DoOneToMany(const std::string &prefix, const std::string &command, std::deque<std::string> ¶ms) +{ + std::string FullLine = ":" + prefix + " " + command; + unsigned int words = params.size(); + for (unsigned int x = 0; x < words; x++) + { + FullLine = FullLine + " " + params[x]; + } + unsigned int items = this->TreeRoot->ChildCount(); + for (unsigned int x = 0; x < items; x++) + { + TreeServer* Route = this->TreeRoot->GetChild(x); + if (Route && Route->GetSocket()) + { + TreeSocket* Sock = Route->GetSocket(); + if (Sock) + Sock->WriteLine(FullLine); + } + } + return true; +} + +bool SpanningTreeUtilities::DoOneToMany(const char* prefix, const char* command, std::deque<std::string> ¶ms) +{ + std::string spfx = prefix; + std::string scmd = command; + return this->DoOneToMany(spfx, scmd, params); +} + +bool SpanningTreeUtilities::DoOneToAllButSender(const char* prefix, const char* command, std::deque<std::string> ¶ms, std::string omit) +{ + std::string spfx = prefix; + std::string scmd = command; + return this->DoOneToAllButSender(spfx, scmd, params, omit); +} + +bool SpanningTreeUtilities::DoOneToOne(const std::string &prefix, const std::string &command, std::deque<std::string> ¶ms, std::string target) +{ + TreeServer* Route = this->BestRouteTo(target); + if (Route) + { + std::string FullLine = ":" + prefix + " " + command; + unsigned int words = params.size(); + for (unsigned int x = 0; x < words; x++) + { + FullLine = FullLine + " " + params[x]; + } + if (Route && Route->GetSocket()) + { + TreeSocket* Sock = Route->GetSocket(); + if (Sock) + Sock->WriteLine(FullLine); + } + return true; + } + else + { + return false; + } +} + +void SpanningTreeUtilities::RefreshIPCache() +{ + ValidIPs.clear(); + for (std::vector<Link>::iterator L = LinkBlocks.begin(); L != LinkBlocks.end(); L++) + { + if ((!L->IPAddr.empty()) && (!L->RecvPass.empty()) && (!L->SendPass.empty()) && (!L->Name.empty()) && (L->Port)) + { + ValidIPs.push_back(L->IPAddr); + + if (L->AllowMask.length()) + ValidIPs.push_back(L->AllowMask); + + /* Needs resolving */ + bool ipvalid = true; + QueryType start_type = DNS_QUERY_A; +#ifdef IPV6 + start_type = DNS_QUERY_AAAA; + if (strchr(L->IPAddr.c_str(),':')) + { + in6_addr n; + if (inet_pton(AF_INET6, L->IPAddr.c_str(), &n) < 1) + ipvalid = false; + } + else +#endif + { + in_addr n; + if (inet_aton(L->IPAddr.c_str(),&n) < 1) + ipvalid = false; + } + if (!ipvalid) + { + try + { + bool cached; + SecurityIPResolver* sr = new SecurityIPResolver((Module*)this->Creator, this, ServerInstance, L->IPAddr, *L, cached, start_type); + ServerInstance->AddResolver(sr, cached); + } + catch (...) + { + } + } + } + } +} + +void SpanningTreeUtilities::ReadConfiguration(bool rebind) +{ + ConfigReader* Conf = new ConfigReader(ServerInstance); + if (rebind) + { + for (int j = 0; j < Conf->Enumerate("bind"); j++) + { + std::string Type = Conf->ReadValue("bind","type",j); + std::string IP = Conf->ReadValue("bind","address",j); + std::string Port = Conf->ReadValue("bind","port",j); + std::string transport = Conf->ReadValue("bind","transport",j); + if (Type == "servers") + { + irc::portparser portrange(Port, false); + int portno = -1; + while ((portno = portrange.GetToken())) + { + if (IP == "*") + IP.clear(); + + if ((!transport.empty()) && (hooks.find(transport.c_str()) == hooks.end())) + { + ServerInstance->Log(DEFAULT,"m_spanningtree: WARNING: Can't find transport type '%s' for port %s:%s - maybe you forgot to load it BEFORE m_spanningtree in your config file? - Skipping this port binding", transport.c_str(), IP.c_str(), Port.c_str()); + break; + } + + TreeSocket* listener = new TreeSocket(this, ServerInstance, IP.c_str(), portno, true, 10, transport.empty() ? NULL : hooks[transport.c_str()]); + if (listener->GetState() == I_LISTENING) + { + ServerInstance->Log(DEFAULT,"m_spanningtree: Binding server port %s:%d successful!", IP.c_str(), portno); + Bindings.push_back(listener); + } + else + { + ServerInstance->Log(DEFAULT,"m_spanningtree: Warning: Failed to bind server port: %s:%d: %s",IP.c_str(), portno, strerror(errno)); + listener->Close(); + DELETE(listener); + } + } + } + } + } + FlatLinks = Conf->ReadFlag("options","flatlinks",0); + HideULines = Conf->ReadFlag("options","hideulines",0); + AnnounceTSChange = Conf->ReadFlag("options","announcets",0); + EnableTimeSync = Conf->ReadFlag("timesync","enable",0); + MasterTime = Conf->ReadFlag("timesync", "master", 0); + ChallengeResponse = !Conf->ReadFlag("options", "disablehmac", 0); + quiet_bursts = Conf->ReadFlag("options", "quietbursts", 0); + PingWarnTime = Conf->ReadInteger("options", "pingwarning", 0, true); + + if (PingWarnTime < 0 || PingWarnTime > 59) + PingWarnTime = 0; + + LinkBlocks.clear(); + ValidIPs.clear(); + for (int j = 0; j < Conf->Enumerate("link"); j++) + { + Link L; + std::string Allow = Conf->ReadValue("link", "allowmask", j); + L.Name = (Conf->ReadValue("link", "name", j)).c_str(); + L.AllowMask = Allow; + L.IPAddr = Conf->ReadValue("link", "ipaddr", j); + L.FailOver = Conf->ReadValue("link", "failover", j).c_str(); + L.Port = Conf->ReadInteger("link", "port", j, true); + L.SendPass = Conf->ReadValue("link", "sendpass", j); + L.RecvPass = Conf->ReadValue("link", "recvpass", j); + L.AutoConnect = Conf->ReadInteger("link", "autoconnect", j, true); + L.HiddenFromStats = Conf->ReadFlag("link", "statshidden", j); + L.Timeout = Conf->ReadInteger("link", "timeout", j, true); + L.Hook = Conf->ReadValue("link", "transport", j); + L.Bind = Conf->ReadValue("link", "bind", j); + L.Hidden = Conf->ReadFlag("link", "hidden", j); + + if ((!L.Hook.empty()) && (hooks.find(L.Hook.c_str()) == hooks.end())) + { + ServerInstance->Log(DEFAULT,"m_spanningtree: WARNING: Can't find transport type '%s' for link '%s' - maybe you forgot to load it BEFORE m_spanningtree in your config file? Skipping <link> tag completely.", + L.Hook.c_str(), L.Name.c_str()); + continue; + + } + + L.NextConnectTime = time(NULL) + L.AutoConnect; + /* Bugfix by brain, do not allow people to enter bad configurations */ + if (L.Name != ServerInstance->Config->ServerName) + { + if ((!L.IPAddr.empty()) && (!L.RecvPass.empty()) && (!L.SendPass.empty()) && (!L.Name.empty()) && (L.Port)) + { + ValidIPs.push_back(L.IPAddr); + + if (Allow.length()) + ValidIPs.push_back(Allow); + + /* Needs resolving */ + bool ipvalid = true; + QueryType start_type = DNS_QUERY_A; +#ifdef IPV6 + start_type = DNS_QUERY_AAAA; + if (strchr(L.IPAddr.c_str(),':')) + { + in6_addr n; + if (inet_pton(AF_INET6, L.IPAddr.c_str(), &n) < 1) + ipvalid = false; + } + else + { + in_addr n; + if (inet_aton(L.IPAddr.c_str(),&n) < 1) + ipvalid = false; + } +#else + in_addr n; + if (inet_aton(L.IPAddr.c_str(),&n) < 1) + ipvalid = false; +#endif + + if (!ipvalid) + { + try + { + bool cached; + SecurityIPResolver* sr = new SecurityIPResolver((Module*)this->Creator, this, ServerInstance, L.IPAddr, L, cached, start_type); + ServerInstance->AddResolver(sr, cached); + } + catch (...) + { + } + } + + LinkBlocks.push_back(L); + } + else + { + if (L.IPAddr.empty()) + { + ServerInstance->Log(DEFAULT,"Invalid configuration for server '%s', IP address not defined!",L.Name.c_str()); + } + else if (L.RecvPass.empty()) + { + ServerInstance->Log(DEFAULT,"Invalid configuration for server '%s', recvpass not defined!",L.Name.c_str()); + } + else if (L.SendPass.empty()) + { + ServerInstance->Log(DEFAULT,"Invalid configuration for server '%s', sendpass not defined!",L.Name.c_str()); + } + else if (L.Name.empty()) + { + ServerInstance->Log(DEFAULT,"Invalid configuration, link tag without a name!"); + } + else if (!L.Port) + { + ServerInstance->Log(DEFAULT,"Invalid configuration for server '%s', no port specified!",L.Name.c_str()); + } + } + } + else + { + ServerInstance->Log(DEFAULT,"Invalid configuration for server '%s', link tag has the same server name as the local server!",L.Name.c_str()); + } + } + DELETE(Conf); +} + +void SpanningTreeUtilities::DoFailOver(Link* x) +{ + if (x->FailOver.length()) + { + if (x->FailOver == x->Name) + { + ServerInstance->SNO->WriteToSnoMask('l',"FAILOVER: Some muppet configured the failover for server \002%s\002 to point at itself. Not following it!", x->Name.c_str()); + return; + } + Link* TryThisOne = this->FindLink(x->FailOver.c_str()); + if (TryThisOne) + { + ServerInstance->SNO->WriteToSnoMask('l',"FAILOVER: Trying failover link for \002%s\002: \002%s\002...", x->Name.c_str(), TryThisOne->Name.c_str()); + Creator->ConnectServer(TryThisOne); + } + else + { + ServerInstance->SNO->WriteToSnoMask('l',"FAILOVER: Invalid failover server specified for server \002%s\002, will not follow!", x->Name.c_str()); + } + } +} + +Link* SpanningTreeUtilities::FindLink(const std::string& name) +{ + for (std::vector<Link>::iterator x = LinkBlocks.begin(); x < LinkBlocks.end(); x++) + { + if (ServerInstance->MatchText(x->Name.c_str(), name.c_str())) + { + return &(*x); + } + } + return NULL; +} + diff --git a/src/modules/m_spanningtree/utils.h b/src/modules/m_spanningtree/utils.h index 48146e89e..cb783a81a 100644 --- a/src/modules/m_spanningtree/utils.h +++ b/src/modules/m_spanningtree/utils.h @@ -1 +1,194 @@ -/* +------------------------------------+
* | 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.
*
* ---------------------------------------------------
*/
#ifndef __ST__UTIL__
#define __ST__UTIL__
#include "configreader.h"
#include "users.h"
#include "channels.h"
#include "modules.h"
#include "inspircd.h"
/* Foward declarations */
class TreeServer;
class TreeSocket;
class Link;
class ModuleSpanningTree;
/* This hash_map holds the hash equivalent of the server
* tree, used for rapid linear lookups.
*/
#ifdef WINDOWS
typedef nspace::hash_map<std::string, TreeServer*, nspace::hash_compare<string, less<string> > > server_hash;
#else
typedef nspace::hash_map<std::string, TreeServer*, nspace::hash<string>, irc::StrHashComp> server_hash;
#endif
typedef std::map<TreeServer*,TreeServer*> TreeServerList;
/** A group of modules that implement InspSocketHook
* that we can use to hook our server to server connections.
*/
typedef std::map<irc::string, Module*> hookmodules;
/** Contains helper functions and variables for this module,
* and keeps them out of the global namespace
*/
class SpanningTreeUtilities
{
private:
/** Creator server
*/
InspIRCd* ServerInstance;
public:
/** Creator module
*/
ModuleSpanningTree* Creator;
/** Remote servers that are currently bursting
*/
server_hash RemoteServersBursting;
/** Flatten links and /MAP for non-opers
*/
bool FlatLinks;
/** Hide U-Lined servers in /MAP and /LINKS
*/
bool HideULines;
/** Announce TS changes to channels on merge
*/
bool AnnounceTSChange;
/** Synchronize timestamps between servers
*/
bool EnableTimeSync;
/** Make snomasks +CQ quiet during bursts and splits
*/
bool quiet_bursts;
/** Socket bindings for listening sockets
*/
std::vector<TreeSocket*> Bindings;
/* Number of seconds that a server can go without ping
* before opers are warned of high latency.
*/
int PingWarnTime;
/** This variable represents the root of the server tree
*/
TreeServer *TreeRoot;
/** IPs allowed to link to us
*/
std::vector<std::string> ValidIPs;
/** Hash of currently connected servers by name
*/
server_hash serverlist;
/** Hash of servers currently bursting but not initialized as connected
*/
std::map<irc::string,TreeSocket*> burstingserverlist;
/** Holds the data from the <link> tags in the conf
*/
std::vector<Link> LinkBlocks;
/** Holds a bitmask of queued xline types waiting to be applied.
* Will be a mask containing values APPLY_GLINES, APPLY_KLINES,
* APPLY_QLINES and APPLY_ZLINES.
*/
int lines_to_apply;
/** If this is true, this server is the master sync server for time
* synching - e.g. it is the server with its clock correct. It will
* send out the correct time at intervals.
*/
bool MasterTime;
/** List of module pointers which can provide I/O abstraction
*/
hookmodules hooks;
/** List of module names which can provide I/O abstraction
*/
std::vector<std::string> hooknames;
/** True (default) if we are to use challenge-response HMAC
* to authenticate passwords.
*
* NOTE: This defaults to on, but should be turned off if
* you are linking to an older version of inspircd.
*/
bool ChallengeResponse;
/** Initialise utility class
*/
SpanningTreeUtilities(InspIRCd* Instance, ModuleSpanningTree* Creator);
/** Destroy class and free listeners etc
*/
~SpanningTreeUtilities();
/** Send a message from this server to one other local or remote
*/
bool DoOneToOne(const std::string &prefix, const std::string &command, std::deque<std::string> ¶ms, std::string target);
/** Send a message from this server to all but one other, local or remote
*/
bool DoOneToAllButSender(const std::string &prefix, const std::string &command, std::deque<std::string> ¶ms, std::string omit);
/** Send a message from this server to all but one other, local or remote
*/
bool DoOneToAllButSender(const char* prefix, const char* command, std::deque<std::string> ¶ms, std::string omit);
/** Send a message from this server to all others
*/
bool DoOneToMany(const std::string &prefix, const std::string &command, std::deque<std::string> ¶ms);
/** Send a message from this server to all others
*/
bool DoOneToMany(const char* prefix, const char* command, std::deque<std::string> ¶ms);
/** Send a message from this server to all others, without doing any processing on the command (e.g. send it as-is with colons and all)
*/
bool DoOneToAllButSenderRaw(const std::string &data, const std::string &omit, const std::string &prefix, const irc::string &command, std::deque<std::string> ¶ms);
/** Read the spanningtree module's tags from the config file
*/
void ReadConfiguration(bool rebind);
/** Add a server to the server list for GetListOfServersForChannel
*/
void AddThisServer(TreeServer* server, TreeServerList &list);
/** Compile a list of servers which contain members of channel c
*/
void GetListOfServersForChannel(chanrec* c, TreeServerList &list, char status, const CUList &exempt_list);
/** Find a server by name
*/
TreeServer* FindServer(const std::string &ServerName);
/** Find a remote bursting server by name
*/
TreeServer* FindRemoteBurstServer(TreeServer* Server);
/** Set a remote server to bursting or not bursting
*/
void SetRemoteBursting(TreeServer* Server, bool bursting);
/** Find a route to a server by name
*/
TreeServer* BestRouteTo(const std::string &ServerName);
/** Find a server by glob mask
*/
TreeServer* FindServerMask(const std::string &ServerName);
/** Returns true if this is a server name we recognise
*/
bool IsServer(const std::string &ServerName);
/** Attempt to connect to the failover link of link x
*/
void DoFailOver(Link* x);
/** Find a link tag from a server name
*/
Link* FindLink(const std::string& name);
/** Refresh the IP cache used for allowing inbound connections
*/
void RefreshIPCache();
TreeSocket* FindBurstingServer(const std::string &ServerName);
void AddBurstingServer(const std::string &ServerName, TreeSocket* s);
void DelBurstingServer(TreeSocket* s);
};
#endif
\ No newline at end of file +/* +------------------------------------+ + * | 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. + * + * --------------------------------------------------- + */ + +#ifndef __ST__UTIL__ +#define __ST__UTIL__ + +#include "configreader.h" +#include "users.h" +#include "channels.h" +#include "modules.h" +#include "inspircd.h" + +/* Foward declarations */ +class TreeServer; +class TreeSocket; +class Link; +class ModuleSpanningTree; + +/* This hash_map holds the hash equivalent of the server + * tree, used for rapid linear lookups. + */ +#ifdef WINDOWS +typedef nspace::hash_map<std::string, TreeServer*, nspace::hash_compare<string, less<string> > > server_hash; +#else +typedef nspace::hash_map<std::string, TreeServer*, nspace::hash<string>, irc::StrHashComp> server_hash; +#endif + +typedef std::map<TreeServer*,TreeServer*> TreeServerList; + +/** A group of modules that implement InspSocketHook + * that we can use to hook our server to server connections. + */ +typedef std::map<irc::string, Module*> hookmodules; + +/** Contains helper functions and variables for this module, + * and keeps them out of the global namespace + */ +class SpanningTreeUtilities +{ + private: + /** Creator server + */ + InspIRCd* ServerInstance; + public: + /** Creator module + */ + ModuleSpanningTree* Creator; + /** Remote servers that are currently bursting + */ + server_hash RemoteServersBursting; + /** Flatten links and /MAP for non-opers + */ + bool FlatLinks; + /** Hide U-Lined servers in /MAP and /LINKS + */ + bool HideULines; + /** Announce TS changes to channels on merge + */ + bool AnnounceTSChange; + /** Synchronize timestamps between servers + */ + bool EnableTimeSync; + /** Make snomasks +CQ quiet during bursts and splits + */ + bool quiet_bursts; + /** Socket bindings for listening sockets + */ + std::vector<TreeSocket*> Bindings; + /* Number of seconds that a server can go without ping + * before opers are warned of high latency. + */ + int PingWarnTime; + /** This variable represents the root of the server tree + */ + TreeServer *TreeRoot; + /** IPs allowed to link to us + */ + std::vector<std::string> ValidIPs; + /** Hash of currently connected servers by name + */ + server_hash serverlist; + /** Hash of servers currently bursting but not initialized as connected + */ + std::map<irc::string,TreeSocket*> burstingserverlist; + /** Holds the data from the <link> tags in the conf + */ + std::vector<Link> LinkBlocks; + /** Holds a bitmask of queued xline types waiting to be applied. + * Will be a mask containing values APPLY_GLINES, APPLY_KLINES, + * APPLY_QLINES and APPLY_ZLINES. + */ + int lines_to_apply; + + /** If this is true, this server is the master sync server for time + * synching - e.g. it is the server with its clock correct. It will + * send out the correct time at intervals. + */ + bool MasterTime; + + /** List of module pointers which can provide I/O abstraction + */ + hookmodules hooks; + + /** List of module names which can provide I/O abstraction + */ + std::vector<std::string> hooknames; + + /** True (default) if we are to use challenge-response HMAC + * to authenticate passwords. + * + * NOTE: This defaults to on, but should be turned off if + * you are linking to an older version of inspircd. + */ + bool ChallengeResponse; + + /** Initialise utility class + */ + SpanningTreeUtilities(InspIRCd* Instance, ModuleSpanningTree* Creator); + /** Destroy class and free listeners etc + */ + ~SpanningTreeUtilities(); + /** Send a message from this server to one other local or remote + */ + bool DoOneToOne(const std::string &prefix, const std::string &command, std::deque<std::string> ¶ms, std::string target); + /** Send a message from this server to all but one other, local or remote + */ + bool DoOneToAllButSender(const std::string &prefix, const std::string &command, std::deque<std::string> ¶ms, std::string omit); + /** Send a message from this server to all but one other, local or remote + */ + bool DoOneToAllButSender(const char* prefix, const char* command, std::deque<std::string> ¶ms, std::string omit); + /** Send a message from this server to all others + */ + bool DoOneToMany(const std::string &prefix, const std::string &command, std::deque<std::string> ¶ms); + /** Send a message from this server to all others + */ + bool DoOneToMany(const char* prefix, const char* command, std::deque<std::string> ¶ms); + /** Send a message from this server to all others, without doing any processing on the command (e.g. send it as-is with colons and all) + */ + bool DoOneToAllButSenderRaw(const std::string &data, const std::string &omit, const std::string &prefix, const irc::string &command, std::deque<std::string> ¶ms); + /** Read the spanningtree module's tags from the config file + */ + void ReadConfiguration(bool rebind); + /** Add a server to the server list for GetListOfServersForChannel + */ + void AddThisServer(TreeServer* server, TreeServerList &list); + /** Compile a list of servers which contain members of channel c + */ + void GetListOfServersForChannel(chanrec* c, TreeServerList &list, char status, const CUList &exempt_list); + /** Find a server by name + */ + TreeServer* FindServer(const std::string &ServerName); + /** Find a remote bursting server by name + */ + TreeServer* FindRemoteBurstServer(TreeServer* Server); + /** Set a remote server to bursting or not bursting + */ + void SetRemoteBursting(TreeServer* Server, bool bursting); + /** Find a route to a server by name + */ + TreeServer* BestRouteTo(const std::string &ServerName); + /** Find a server by glob mask + */ + TreeServer* FindServerMask(const std::string &ServerName); + /** Returns true if this is a server name we recognise + */ + bool IsServer(const std::string &ServerName); + /** Attempt to connect to the failover link of link x + */ + void DoFailOver(Link* x); + /** Find a link tag from a server name + */ + Link* FindLink(const std::string& name); + /** Refresh the IP cache used for allowing inbound connections + */ + void RefreshIPCache(); + + TreeSocket* FindBurstingServer(const std::string &ServerName); + + void AddBurstingServer(const std::string &ServerName, TreeSocket* s); + + void DelBurstingServer(TreeSocket* s); +}; + +#endif diff --git a/src/modules/m_spy.cpp b/src/modules/m_spy.cpp index 11257c437..20b59977c 100644 --- a/src/modules/m_spy.cpp +++ b/src/modules/m_spy.cpp @@ -1 +1,163 @@ -/* +------------------------------------+
* | 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.
*
* ---------------------------------------------------
*/
/* NO, THIS MODULE DOES NOT SPY ON CHANNELS OR USERS.
* IT JUST ALLOWS OPERS TO SEE +s CHANNELS IN LIST AND
* WHOIS, WHICH IS SUPPORTED BY MOST IRCDS IN CORE.
*/
/* $ModDesc: Provides SPYLIST and SPYNAMES capability, allowing opers to see who's in +s channels */
#include "inspircd.h"
#include "users.h"
#include "channels.h"
#include "modules.h"
#include "wildcard.h"
void spy_userlist(userrec *user, chanrec *c)
{
char list[MAXBUF];
size_t dlen, curlen;
dlen = curlen = snprintf(list,MAXBUF,"353 %s = %s :", user->nick, c->name);
int numusers = 0;
char* ptr = list + dlen;
CUList *ulist= c->GetUsers();
for (CUList::iterator i = ulist->begin(); i != ulist->end(); i++)
{
size_t ptrlen = snprintf(ptr, MAXBUF, "%s%s ", c->GetPrefixChar(i->first), i->first->nick);
curlen += ptrlen;
ptr += ptrlen;
numusers++;
if (curlen > (480-NICKMAX))
{
/* list overflowed into multiple numerics */
user->WriteServ(std::string(list));
/* reset our lengths */
dlen = curlen = snprintf(list,MAXBUF,"353 %s = %s :", user->nick, c->name);
ptr = list + dlen;
ptrlen = 0;
numusers = 0;
}
}
/* if whats left in the list isnt empty, send it */
if (numusers)
{
user->WriteServ(std::string(list));
}
user->WriteServ("366 %s %s :End of /NAMES list.", user->nick, c->name);
}
/** Handle /SPYLIST
*/
class cmd_spylist : public command_t
{
public:
cmd_spylist (InspIRCd* Instance) : command_t(Instance,"SPYLIST", 'o', 0)
{
this->source = "m_spy.so";
syntax.clear();
}
CmdResult Handle (const char** parameters, int pcnt, userrec *user)
{
ServerInstance->WriteOpers("*** Oper %s used SPYLIST to list +s/+p channels and keys.",user->nick);
user->WriteServ("321 %s Channel :Users Name",user->nick);
for (chan_hash::const_iterator i = ServerInstance->chanlist->begin(); i != ServerInstance->chanlist->end(); i++)
{
if (pcnt && !match(i->second->name, parameters[0]))
continue;
user->WriteServ("322 %s %s %d :[+%s] %s",user->nick,i->second->name,i->second->GetUserCounter(),i->second->ChanModes(true),i->second->topic);
}
user->WriteServ("323 %s :End of channel list.",user->nick);
/* Dont send out across the network */
return CMD_FAILURE;
}
};
/** Handle /SPYNAMES
*/
class cmd_spynames : public command_t
{
public:
cmd_spynames (InspIRCd* Instance) : command_t(Instance,"SPYNAMES", 'o', 0)
{
this->source = "m_spy.so";
syntax = "{<channel>{,<channel>}}";
}
CmdResult Handle (const char** parameters, int pcnt, userrec *user)
{
chanrec* c = NULL;
if (!pcnt)
{
user->WriteServ("366 %s * :End of /NAMES list.",user->nick);
return CMD_FAILURE;
}
if (ServerInstance->Parser->LoopCall(user, this, parameters, pcnt, 0))
return CMD_FAILURE;
c = ServerInstance->FindChan(parameters[0]);
if (c)
{
ServerInstance->WriteOpers("*** Oper %s used SPYNAMES to view the users on %s", user->nick, parameters[0]);
spy_userlist(user,c);
}
else
{
user->WriteServ("401 %s %s :No such nick/channel",user->nick, parameters[0]);
}
return CMD_FAILURE;
}
};
class ModuleSpy : public Module
{
cmd_spylist *mycommand;
cmd_spynames *mycommand2;
public:
ModuleSpy(InspIRCd* Me) : Module(Me)
{
mycommand = new cmd_spylist(ServerInstance);
mycommand2 = new cmd_spynames(ServerInstance);
ServerInstance->AddCommand(mycommand);
ServerInstance->AddCommand(mycommand2);
}
virtual ~ModuleSpy()
{
}
virtual Version GetVersion()
{
return Version(1, 1, 0, 0, VF_VENDOR, API_VERSION);
}
};
MODULE_INIT(ModuleSpy)
\ No newline at end of file +/* +------------------------------------+ + * | 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. + * + * --------------------------------------------------- + */ + +/* NO, THIS MODULE DOES NOT SPY ON CHANNELS OR USERS. + * IT JUST ALLOWS OPERS TO SEE +s CHANNELS IN LIST AND + * WHOIS, WHICH IS SUPPORTED BY MOST IRCDS IN CORE. + */ + +/* $ModDesc: Provides SPYLIST and SPYNAMES capability, allowing opers to see who's in +s channels */ + +#include "inspircd.h" +#include "users.h" +#include "channels.h" +#include "modules.h" +#include "wildcard.h" + +void spy_userlist(userrec *user, chanrec *c) +{ + char list[MAXBUF]; + size_t dlen, curlen; + + dlen = curlen = snprintf(list,MAXBUF,"353 %s = %s :", user->nick, c->name); + + int numusers = 0; + char* ptr = list + dlen; + + CUList *ulist= c->GetUsers(); + + for (CUList::iterator i = ulist->begin(); i != ulist->end(); i++) + { + size_t ptrlen = snprintf(ptr, MAXBUF, "%s%s ", c->GetPrefixChar(i->first), i->first->nick); + + curlen += ptrlen; + ptr += ptrlen; + + numusers++; + + if (curlen > (480-NICKMAX)) + { + /* list overflowed into multiple numerics */ + user->WriteServ(std::string(list)); + + /* reset our lengths */ + dlen = curlen = snprintf(list,MAXBUF,"353 %s = %s :", user->nick, c->name); + ptr = list + dlen; + + ptrlen = 0; + numusers = 0; + } + } + + /* if whats left in the list isnt empty, send it */ + if (numusers) + { + user->WriteServ(std::string(list)); + } + + user->WriteServ("366 %s %s :End of /NAMES list.", user->nick, c->name); + +} + +/** Handle /SPYLIST + */ +class cmd_spylist : public command_t +{ + public: + cmd_spylist (InspIRCd* Instance) : command_t(Instance,"SPYLIST", 'o', 0) + { + this->source = "m_spy.so"; + syntax.clear(); + } + + CmdResult Handle (const char** parameters, int pcnt, userrec *user) + { + ServerInstance->WriteOpers("*** Oper %s used SPYLIST to list +s/+p channels and keys.",user->nick); + user->WriteServ("321 %s Channel :Users Name",user->nick); + for (chan_hash::const_iterator i = ServerInstance->chanlist->begin(); i != ServerInstance->chanlist->end(); i++) + { + if (pcnt && !match(i->second->name, parameters[0])) + continue; + user->WriteServ("322 %s %s %d :[+%s] %s",user->nick,i->second->name,i->second->GetUserCounter(),i->second->ChanModes(true),i->second->topic); + } + user->WriteServ("323 %s :End of channel list.",user->nick); + + /* Dont send out across the network */ + return CMD_FAILURE; + } +}; + +/** Handle /SPYNAMES + */ +class cmd_spynames : public command_t +{ + public: + cmd_spynames (InspIRCd* Instance) : command_t(Instance,"SPYNAMES", 'o', 0) + { + this->source = "m_spy.so"; + syntax = "{<channel>{,<channel>}}"; + } + + CmdResult Handle (const char** parameters, int pcnt, userrec *user) + { + chanrec* c = NULL; + + if (!pcnt) + { + user->WriteServ("366 %s * :End of /NAMES list.",user->nick); + return CMD_FAILURE; + } + + if (ServerInstance->Parser->LoopCall(user, this, parameters, pcnt, 0)) + return CMD_FAILURE; + + c = ServerInstance->FindChan(parameters[0]); + if (c) + { + ServerInstance->WriteOpers("*** Oper %s used SPYNAMES to view the users on %s", user->nick, parameters[0]); + spy_userlist(user,c); + } + else + { + user->WriteServ("401 %s %s :No such nick/channel",user->nick, parameters[0]); + } + + return CMD_FAILURE; + } +}; + +class ModuleSpy : public Module +{ + cmd_spylist *mycommand; + cmd_spynames *mycommand2; + public: + ModuleSpy(InspIRCd* Me) : Module(Me) + { + + mycommand = new cmd_spylist(ServerInstance); + mycommand2 = new cmd_spynames(ServerInstance); + ServerInstance->AddCommand(mycommand); + ServerInstance->AddCommand(mycommand2); + } + + virtual ~ModuleSpy() + { + } + + virtual Version GetVersion() + { + return Version(1, 1, 0, 0, VF_VENDOR, API_VERSION); + } +}; + +MODULE_INIT(ModuleSpy) diff --git a/src/modules/m_ssl_dummy.cpp b/src/modules/m_ssl_dummy.cpp index 3b872b81c..fb3032da2 100644 --- a/src/modules/m_ssl_dummy.cpp +++ b/src/modules/m_ssl_dummy.cpp @@ -1 +1,84 @@ -/* +------------------------------------+
* | 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 "users.h"
#include "modules.h"
/* $ModDesc: Makes remote /whoises to SSL servers work on a non-ssl server */
class ModuleSSLDummy : public Module
{
char* dummy;
public:
ModuleSSLDummy(InspIRCd* Me) : Module(Me)
{
}
virtual ~ModuleSSLDummy()
{
}
virtual Version GetVersion()
{
return Version(1, 0, 0, 0, VF_VENDOR, API_VERSION);
}
void Implements(char* List)
{
List[I_OnSyncUserMetaData] = List[I_OnDecodeMetaData] = List[I_OnWhois] = 1;
}
// :kenny.chatspike.net 320 Om Epy|AFK :is a Secure Connection
virtual void OnWhois(userrec* source, userrec* dest)
{
if(dest->GetExt("ssl", dummy))
{
ServerInstance->SendWhoisLine(source, dest, 320, "%s %s :is using a secure connection", source->nick, dest->nick);
}
}
virtual void OnSyncUserMetaData(userrec* user, Module* proto, void* opaque, const std::string &extname, bool displayable)
{
// check if the linking module wants to know about OUR metadata
if(extname == "ssl")
{
// check if this user has an ssl field to send
if(user->GetExt(extname, dummy))
{
// call this function in the linking module, let it format the data how it
// sees fit, and send it on its way. We dont need or want to know how.
proto->ProtoSendMetaData(opaque, TYPE_USER, user, extname, displayable ? "Enabled" : "ON");
}
}
}
virtual void OnDecodeMetaData(int target_type, void* target, const std::string &extname, const std::string &extdata)
{
// check if its our metadata key, and its associated with a user
if ((target_type == TYPE_USER) && (extname == "ssl"))
{
userrec* dest = (userrec*)target;
// if they dont already have an ssl flag, accept the remote server's
if (!dest->GetExt(extname, dummy))
{
dest->Extend(extname, "ON");
}
}
}
};
MODULE_INIT(ModuleSSLDummy)
\ No newline at end of file +/* +------------------------------------+ + * | 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 "users.h" +#include "modules.h" + +/* $ModDesc: Makes remote /whoises to SSL servers work on a non-ssl server */ + +class ModuleSSLDummy : public Module +{ + + char* dummy; + public: + + ModuleSSLDummy(InspIRCd* Me) : Module(Me) + { + + } + + virtual ~ModuleSSLDummy() + { + } + + virtual Version GetVersion() + { + return Version(1, 0, 0, 0, VF_VENDOR, API_VERSION); + } + + void Implements(char* List) + { + List[I_OnSyncUserMetaData] = List[I_OnDecodeMetaData] = List[I_OnWhois] = 1; + } + + // :kenny.chatspike.net 320 Om Epy|AFK :is a Secure Connection + virtual void OnWhois(userrec* source, userrec* dest) + { + if(dest->GetExt("ssl", dummy)) + { + ServerInstance->SendWhoisLine(source, dest, 320, "%s %s :is using a secure connection", source->nick, dest->nick); + } + } + + virtual void OnSyncUserMetaData(userrec* user, Module* proto, void* opaque, const std::string &extname, bool displayable) + { + // check if the linking module wants to know about OUR metadata + if(extname == "ssl") + { + // check if this user has an ssl field to send + if(user->GetExt(extname, dummy)) + { + // call this function in the linking module, let it format the data how it + // sees fit, and send it on its way. We dont need or want to know how. + proto->ProtoSendMetaData(opaque, TYPE_USER, user, extname, displayable ? "Enabled" : "ON"); + } + } + } + + virtual void OnDecodeMetaData(int target_type, void* target, const std::string &extname, const std::string &extdata) + { + // check if its our metadata key, and its associated with a user + if ((target_type == TYPE_USER) && (extname == "ssl")) + { + userrec* dest = (userrec*)target; + // if they dont already have an ssl flag, accept the remote server's + if (!dest->GetExt(extname, dummy)) + { + dest->Extend(extname, "ON"); + } + } + } +}; + +MODULE_INIT(ModuleSSLDummy) diff --git a/src/modules/m_sslmodes.cpp b/src/modules/m_sslmodes.cpp index 0e06aa314..c8eee5a03 100644 --- a/src/modules/m_sslmodes.cpp +++ b/src/modules/m_sslmodes.cpp @@ -1 +1,145 @@ -/* +------------------------------------+
* | 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 "users.h"
#include "channels.h"
#include "modules.h"
/* $ModDesc: Provides support for unreal-style channel mode +z */
static char* dummy;
/** Handle channel mode +z
*/
class SSLMode : public ModeHandler
{
public:
SSLMode(InspIRCd* Instance) : ModeHandler(Instance, 'z', 0, 0, false, MODETYPE_CHANNEL, false) { }
ModeAction OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string ¶meter, bool adding)
{
if (adding)
{
if (!channel->IsModeSet('z'))
{
if (IS_LOCAL(source))
{
CUList* userlist = channel->GetUsers();
for(CUList::iterator i = userlist->begin(); i != userlist->end(); i++)
{
if(!i->first->GetExt("ssl", dummy))
{
source->WriteServ("490 %s %s :all members of the channel must be connected via SSL", source->nick, channel->name);
return MODEACTION_DENY;
}
}
}
channel->SetMode('z',true);
return MODEACTION_ALLOW;
}
else
{
return MODEACTION_DENY;
}
}
else
{
if (channel->IsModeSet('z'))
{
channel->SetMode('z',false);
return MODEACTION_ALLOW;
}
return MODEACTION_DENY;
}
}
};
class ModuleSSLModes : public Module
{
SSLMode* sslm;
public:
ModuleSSLModes(InspIRCd* Me)
: Module(Me)
{
sslm = new SSLMode(ServerInstance);
if (!ServerInstance->AddMode(sslm, 'z'))
throw ModuleException("Could not add new modes!");
}
void Implements(char* List)
{
List[I_OnUserPreJoin] = 1;
}
virtual int OnUserPreJoin(userrec* user, chanrec* chan, const char* cname, std::string &privs)
{
if(chan && chan->IsModeSet('z'))
{
if(user->GetExt("ssl", dummy))
{
// Let them in
return 0;
}
else
{
// Deny
user->WriteServ( "489 %s %s :Cannot join channel; SSL users only (+z)", user->nick, cname);
return 1;
}
}
return 0;
}
virtual ~ModuleSSLModes()
{
ServerInstance->Modes->DelMode(sslm);
DELETE(sslm);
}
virtual Version GetVersion()
{
return Version(1, 1, 0, 0, VF_COMMON | VF_VENDOR, API_VERSION);
}
};
class ModuleSSLModesFactory : public ModuleFactory
{
public:
ModuleSSLModesFactory()
{
}
~ModuleSSLModesFactory()
{
}
virtual Module* CreateModule(InspIRCd* Me)
{
return new ModuleSSLModes(Me);
}
};
extern "C" DllExport void * init_module( void )
{
return new ModuleSSLModesFactory;
}
\ No newline at end of file +/* +------------------------------------+ + * | 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 "users.h" +#include "channels.h" +#include "modules.h" + +/* $ModDesc: Provides support for unreal-style channel mode +z */ + +static char* dummy; + +/** Handle channel mode +z + */ +class SSLMode : public ModeHandler +{ + public: + SSLMode(InspIRCd* Instance) : ModeHandler(Instance, 'z', 0, 0, false, MODETYPE_CHANNEL, false) { } + + ModeAction OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string ¶meter, bool adding) + { + if (adding) + { + if (!channel->IsModeSet('z')) + { + if (IS_LOCAL(source)) + { + CUList* userlist = channel->GetUsers(); + for(CUList::iterator i = userlist->begin(); i != userlist->end(); i++) + { + if(!i->first->GetExt("ssl", dummy)) + { + source->WriteServ("490 %s %s :all members of the channel must be connected via SSL", source->nick, channel->name); + return MODEACTION_DENY; + } + } + } + channel->SetMode('z',true); + return MODEACTION_ALLOW; + } + else + { + return MODEACTION_DENY; + } + } + else + { + if (channel->IsModeSet('z')) + { + channel->SetMode('z',false); + return MODEACTION_ALLOW; + } + + return MODEACTION_DENY; + } + } +}; + +class ModuleSSLModes : public Module +{ + + SSLMode* sslm; + + public: + ModuleSSLModes(InspIRCd* Me) + : Module(Me) + { + + + sslm = new SSLMode(ServerInstance); + if (!ServerInstance->AddMode(sslm, 'z')) + throw ModuleException("Could not add new modes!"); + } + + void Implements(char* List) + { + List[I_OnUserPreJoin] = 1; + } + + virtual int OnUserPreJoin(userrec* user, chanrec* chan, const char* cname, std::string &privs) + { + if(chan && chan->IsModeSet('z')) + { + if(user->GetExt("ssl", dummy)) + { + // Let them in + return 0; + } + else + { + // Deny + user->WriteServ( "489 %s %s :Cannot join channel; SSL users only (+z)", user->nick, cname); + return 1; + } + } + + return 0; + } + + virtual ~ModuleSSLModes() + { + ServerInstance->Modes->DelMode(sslm); + DELETE(sslm); + } + + virtual Version GetVersion() + { + return Version(1, 1, 0, 0, VF_COMMON | VF_VENDOR, API_VERSION); + } +}; + + +class ModuleSSLModesFactory : public ModuleFactory +{ + public: + ModuleSSLModesFactory() + { + } + + ~ModuleSSLModesFactory() + { + } + + virtual Module* CreateModule(InspIRCd* Me) + { + return new ModuleSSLModes(Me); + } + +}; + + +extern "C" DllExport void * init_module( void ) +{ + return new ModuleSSLModesFactory; +} diff --git a/src/modules/m_stripcolor.cpp b/src/modules/m_stripcolor.cpp index 6f1d7b130..aad253bc7 100644 --- a/src/modules/m_stripcolor.cpp +++ b/src/modules/m_stripcolor.cpp @@ -1 +1,185 @@ -/* +------------------------------------+
* | 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 "users.h"
#include "channels.h"
#include "modules.h"
/* $ModDesc: Provides channel +S mode (strip ansi colour) */
/** Handles channel mode +S
*/
class ChannelStripColor : public ModeHandler
{
public:
ChannelStripColor(InspIRCd* Instance) : ModeHandler(Instance, 'S', 0, 0, false, MODETYPE_CHANNEL, false) { }
ModeAction OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string ¶meter, bool adding)
{
if (adding)
{
if (!channel->IsModeSet('S'))
{
channel->SetMode('S',true);
return MODEACTION_ALLOW;
}
}
else
{
if (channel->IsModeSet('S'))
{
channel->SetMode('S',false);
return MODEACTION_ALLOW;
}
}
return MODEACTION_DENY;
}
};
/** Handles user mode +S
*/
class UserStripColor : public ModeHandler
{
public:
UserStripColor(InspIRCd* Instance) : ModeHandler(Instance, 'S', 0, 0, false, MODETYPE_USER, false) { }
ModeAction OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string ¶meter, bool adding)
{
/* Only opers can change other users modes */
if (source != dest)
return MODEACTION_DENY;
if (adding)
{
if (!dest->IsModeSet('S'))
{
dest->SetMode('S',true);
return MODEACTION_ALLOW;
}
}
else
{
if (dest->IsModeSet('S'))
{
dest->SetMode('S',false);
return MODEACTION_ALLOW;
}
}
return MODEACTION_DENY;
}
};
class ModuleStripColor : public Module
{
bool AllowChanOps;
ChannelStripColor *csc;
UserStripColor *usc;
public:
ModuleStripColor(InspIRCd* Me) : Module(Me)
{
usc = new UserStripColor(ServerInstance);
csc = new ChannelStripColor(ServerInstance);
if (!ServerInstance->AddMode(usc, 'S') || !ServerInstance->AddMode(csc, 'S'))
throw ModuleException("Could not add new modes!");
}
void Implements(char* List)
{
List[I_OnUserPreMessage] = List[I_OnUserPreNotice] = 1;
}
virtual ~ModuleStripColor()
{
ServerInstance->Modes->DelMode(usc);
ServerInstance->Modes->DelMode(csc);
DELETE(usc);
DELETE(csc);
}
virtual void ReplaceLine(std::string &sentence)
{
/* refactor this completely due to SQUIT bug since the old code would strip last char and replace with \0 --peavey */
int seq = 0;
std::string::iterator i,safei;
for (i = sentence.begin(); i != sentence.end(); ++i)
{
if ((*i == 3))
seq = 1;
else if (seq && ( (*i >= '0') && (*i <= '9') || (*i == ',') ) )
{
seq++;
if ( (seq <= 4) && (*i == ',') )
seq = 1;
else if (seq > 3)
seq = 0;
}
else
seq = 0;
if (seq || ((*i == 2) || (*i == 15) || (*i == 22) || (*i == 21) || (*i == 31)))
{
safei = i;
--i;
sentence.erase(safei);
}
}
}
virtual int OnUserPreMessage(userrec* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list)
{
if (!IS_LOCAL(user))
return 0;
bool active = false;
if (target_type == TYPE_USER)
{
userrec* t = (userrec*)dest;
active = t->IsModeSet('S');
}
else if (target_type == TYPE_CHANNEL)
{
chanrec* t = (chanrec*)dest;
// check if we allow ops to bypass filtering, if we do, check if they're opped accordingly.
// note: short circut logic here, don't wreck it. -- w00t
if (!CHANOPS_EXEMPT(ServerInstance, 'S') || CHANOPS_EXEMPT(ServerInstance, 'S') && t->GetStatus(user) != STATUS_OP)
active = t->IsModeSet('S');
}
if (active)
{
this->ReplaceLine(text);
}
return 0;
}
virtual int OnUserPreNotice(userrec* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list)
{
return OnUserPreMessage(user,dest,target_type,text,status,exempt_list);
}
virtual Version GetVersion()
{
return Version(1, 1, 0, 0, VF_COMMON | VF_VENDOR, API_VERSION);
}
};
MODULE_INIT(ModuleStripColor)
\ No newline at end of file +/* +------------------------------------+ + * | 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 "users.h" +#include "channels.h" +#include "modules.h" + +/* $ModDesc: Provides channel +S mode (strip ansi colour) */ + +/** Handles channel mode +S + */ +class ChannelStripColor : public ModeHandler +{ + public: + ChannelStripColor(InspIRCd* Instance) : ModeHandler(Instance, 'S', 0, 0, false, MODETYPE_CHANNEL, false) { } + + ModeAction OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string ¶meter, bool adding) + { + if (adding) + { + if (!channel->IsModeSet('S')) + { + channel->SetMode('S',true); + return MODEACTION_ALLOW; + } + } + else + { + if (channel->IsModeSet('S')) + { + channel->SetMode('S',false); + return MODEACTION_ALLOW; + } + } + + return MODEACTION_DENY; + } +}; + +/** Handles user mode +S + */ +class UserStripColor : public ModeHandler +{ + public: + UserStripColor(InspIRCd* Instance) : ModeHandler(Instance, 'S', 0, 0, false, MODETYPE_USER, false) { } + + ModeAction OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string ¶meter, bool adding) + { + /* Only opers can change other users modes */ + if (source != dest) + return MODEACTION_DENY; + + if (adding) + { + if (!dest->IsModeSet('S')) + { + dest->SetMode('S',true); + return MODEACTION_ALLOW; + } + } + else + { + if (dest->IsModeSet('S')) + { + dest->SetMode('S',false); + return MODEACTION_ALLOW; + } + } + + return MODEACTION_DENY; + } +}; + + +class ModuleStripColor : public Module +{ + bool AllowChanOps; + ChannelStripColor *csc; + UserStripColor *usc; + + public: + ModuleStripColor(InspIRCd* Me) : Module(Me) + { + usc = new UserStripColor(ServerInstance); + csc = new ChannelStripColor(ServerInstance); + + if (!ServerInstance->AddMode(usc, 'S') || !ServerInstance->AddMode(csc, 'S')) + throw ModuleException("Could not add new modes!"); + } + + void Implements(char* List) + { + List[I_OnUserPreMessage] = List[I_OnUserPreNotice] = 1; + } + + virtual ~ModuleStripColor() + { + ServerInstance->Modes->DelMode(usc); + ServerInstance->Modes->DelMode(csc); + DELETE(usc); + DELETE(csc); + } + + virtual void ReplaceLine(std::string &sentence) + { + /* refactor this completely due to SQUIT bug since the old code would strip last char and replace with \0 --peavey */ + int seq = 0; + std::string::iterator i,safei; + for (i = sentence.begin(); i != sentence.end(); ++i) + { + if ((*i == 3)) + seq = 1; + else if (seq && ( (*i >= '0') && (*i <= '9') || (*i == ',') ) ) + { + seq++; + if ( (seq <= 4) && (*i == ',') ) + seq = 1; + else if (seq > 3) + seq = 0; + } + else + seq = 0; + + if (seq || ((*i == 2) || (*i == 15) || (*i == 22) || (*i == 21) || (*i == 31))) + { + safei = i; + --i; + sentence.erase(safei); + } + } + } + + virtual int OnUserPreMessage(userrec* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list) + { + if (!IS_LOCAL(user)) + return 0; + + bool active = false; + if (target_type == TYPE_USER) + { + userrec* t = (userrec*)dest; + active = t->IsModeSet('S'); + } + else if (target_type == TYPE_CHANNEL) + { + chanrec* t = (chanrec*)dest; + + // check if we allow ops to bypass filtering, if we do, check if they're opped accordingly. + // note: short circut logic here, don't wreck it. -- w00t + if (!CHANOPS_EXEMPT(ServerInstance, 'S') || CHANOPS_EXEMPT(ServerInstance, 'S') && t->GetStatus(user) != STATUS_OP) + active = t->IsModeSet('S'); + } + + if (active) + { + this->ReplaceLine(text); + } + + return 0; + } + + virtual int OnUserPreNotice(userrec* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list) + { + return OnUserPreMessage(user,dest,target_type,text,status,exempt_list); + } + + virtual Version GetVersion() + { + return Version(1, 1, 0, 0, VF_COMMON | VF_VENDOR, API_VERSION); + } + +}; + +MODULE_INIT(ModuleStripColor) diff --git a/src/modules/m_svshold.cpp b/src/modules/m_svshold.cpp index f220d1638..4058c04d0 100644 --- a/src/modules/m_svshold.cpp +++ b/src/modules/m_svshold.cpp @@ -1 +1,282 @@ -/* +------------------------------------+
* | 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 <algorithm>
#include "users.h"
#include "channels.h"
#include "modules.h"
#include "configreader.h"
/* $ModDesc: Implements SVSHOLD. Like Q:Lines, but can only be added/removed by Services. */
/** Holds a SVSHold item
*/
class SVSHold : public classbase
{
public:
std::string nickname;
std::string set_by;
time_t set_on;
long length;
std::string reason;
SVSHold()
{
}
SVSHold(const std::string &nn, const std::string &sb, const time_t so, const long ln, const std::string &rs) : nickname(nn), set_by(sb), set_on(so), length(ln), reason(rs)
{
}
};
bool SVSHoldComp(const SVSHold* ban1, const SVSHold* ban2);
typedef std::vector<SVSHold*> SVSHoldlist;
typedef std::map<irc::string, SVSHold*> SVSHoldMap;
/* SVSHolds is declared here, as our type is right above. Don't try move it. */
SVSHoldlist SVSHolds;
SVSHoldMap HoldMap;
/** Handle /SVSHold
*/
class cmd_svshold : public command_t
{
public:
cmd_svshold(InspIRCd* Me) : command_t(Me, "SVSHOLD", 'o', 1)
{
this->source = "m_svshold.so";
this->syntax = "<nickname> [<duration> :<reason>]";
}
CmdResult Handle(const char** parameters, int pcnt, userrec *user)
{
/* syntax: svshold nickname time :reason goes here */
/* 'time' is a human-readable timestring, like 2d3h2s. */
if (!ServerInstance->ULine(user->server))
{
/* don't allow SVSHOLD from non-ulined clients */
return CMD_FAILURE;
}
if (pcnt == 1)
{
SVSHoldMap::iterator n = HoldMap.find(parameters[0]);
if (n != HoldMap.end())
{
/* form: svshold nickname removes a hold. */
for (SVSHoldlist::iterator iter = SVSHolds.begin(); iter != SVSHolds.end(); iter++)
{
if (parameters[0] == assign((*iter)->nickname))
{
unsigned long remaining = 0;
if ((*iter)->length)
{
remaining = ((*iter)->set_on + (*iter)->length) - ServerInstance->Time();
user->WriteServ( "386 %s %s :Removed SVSHOLD with %lu seconds left before expiry (%s)", user->nick, (*iter)->nickname.c_str(), remaining, (*iter)->reason.c_str());
}
else
{
user->WriteServ( "386 %s %s :Removed permanent SVSHOLD (%s)", user->nick, (*iter)->nickname.c_str(), (*iter)->reason.c_str());
}
SVSHolds.erase(iter);
break;
}
}
HoldMap.erase(n);
delete n->second;
}
}
else if (pcnt >= 2)
{
/* full form to add a SVSHold */
if (ServerInstance->IsNick(parameters[0]))
{
// parameters[0] = w00t
// parameters[1] = 1h3m2s
// parameters[2] = Registered nickname
/* Already exists? */
if (HoldMap.find(parameters[0]) != HoldMap.end())
{
user->WriteServ( "385 %s %s :SVSHOLD already exists", user->nick, parameters[0]);
return CMD_FAILURE;
}
long length = ServerInstance->Duration(parameters[1]);
std::string reason = (pcnt > 2) ? parameters[2] : "No reason supplied";
SVSHold* S = new SVSHold(parameters[0], user->nick, ServerInstance->Time(), length, reason);
SVSHolds.push_back(S);
HoldMap[parameters[0]] = S;
std::sort(SVSHolds.begin(), SVSHolds.end(), SVSHoldComp);
if(length > 0)
{
user->WriteServ( "385 %s %s :Added %lu second SVSHOLD (%s)", user->nick, parameters[0], length, reason.c_str());
ServerInstance->WriteOpers("*** %s added %lu second SVSHOLD on %s (%s)", user->nick, length, parameters[0], reason.c_str());
}
else
{
user->WriteServ( "385 %s %s :Added permanent SVSHOLD on %s (%s)", user->nick, parameters[0], parameters[0], reason.c_str());
ServerInstance->WriteOpers("*** %s added permanent SVSHOLD on %s (%s)", user->nick, parameters[0], reason.c_str());
}
}
else
{
/* as this is primarily a Services command, do not provide an error */
return CMD_FAILURE;
}
}
return CMD_SUCCESS;
}
};
bool SVSHoldComp(const SVSHold* ban1, const SVSHold* ban2)
{
return ((ban1->set_on + ban1->length) < (ban2->set_on + ban2->length));
}
class ModuleSVSHold : public Module
{
cmd_svshold *mycommand;
public:
ModuleSVSHold(InspIRCd* Me) : Module(Me)
{
mycommand = new cmd_svshold(Me);
ServerInstance->AddCommand(mycommand);
}
void Implements(char* List)
{
List[I_OnUserPreNick] = List[I_OnSyncOtherMetaData] = List[I_OnDecodeMetaData] = List[I_OnStats] = 1;
}
virtual int OnStats(char symbol, userrec* user, string_list &results)
{
ExpireBans();
if(symbol == 'S')
{
for(SVSHoldlist::iterator iter = SVSHolds.begin(); iter != SVSHolds.end(); iter++)
{
unsigned long remaining = ((*iter)->set_on + (*iter)->length) - ServerInstance->Time();
results.push_back(std::string(ServerInstance->Config->ServerName)+" 210 "+user->nick+" "+(*iter)->nickname.c_str()+" "+(*iter)->set_by+" "+ConvToStr((*iter)->set_on)+" "+ConvToStr((*iter)->length)+" "+ConvToStr(remaining)+" :"+(*iter)->reason);
}
}
return 0;
}
virtual int OnUserPreNick(userrec *user, const std::string &newnick)
{
ExpireBans();
/* check SVSHolds in here, and apply as necessary. */
SVSHoldMap::iterator n = HoldMap.find(assign(newnick));
if (n != HoldMap.end())
{
user->WriteServ( "432 %s %s :Reserved nickname: %s", user->nick, newnick.c_str(), n->second->reason.c_str());
return 1;
}
return 0;
}
virtual void OnSyncOtherMetaData(Module* proto, void* opaque, bool displayable)
{
for(SVSHoldMap::iterator iter = HoldMap.begin(); iter != HoldMap.end(); iter++)
{
proto->ProtoSendMetaData(opaque, TYPE_OTHER, NULL, "SVSHold", EncodeSVSHold(iter->second));
}
}
virtual void OnDecodeMetaData(int target_type, void* target, const std::string &extname, const std::string &extdata)
{
if((target_type == TYPE_OTHER) && (extname == "SVSHold"))
{
SVSHold* S = DecodeSVSHold(extdata); /* NOTE: Allocates a new SVSHold* */
if (HoldMap.find(assign(S->nickname)) == HoldMap.end())
{
SVSHolds.push_back(S);
HoldMap[assign(S->nickname)] = S;
std::sort(SVSHolds.begin(), SVSHolds.end(), SVSHoldComp);
}
else
{
delete S;
}
}
}
virtual ~ModuleSVSHold()
{
}
virtual Version GetVersion()
{
return Version(1,1,0,1,VF_VENDOR|VF_COMMON,API_VERSION);
}
std::string EncodeSVSHold(const SVSHold* ban)
{
std::ostringstream stream;
stream << ban->nickname << " " << ban->set_by << " " << ban->set_on << " " << ban->length << " :" << ban->reason;
return stream.str();
}
SVSHold* DecodeSVSHold(const std::string &data)
{
SVSHold* res = new SVSHold();
int set_on;
irc::tokenstream tokens(data);
tokens.GetToken(res->nickname);
tokens.GetToken(res->set_by);
tokens.GetToken(set_on);
res->set_on = set_on;
tokens.GetToken(res->length);
tokens.GetToken(res->reason);
return res;
}
void ExpireBans()
{
SVSHoldlist::iterator iter,safeiter;
for (iter = SVSHolds.begin(); iter != SVSHolds.end(); iter++)
{
/* 0 == permanent, don't mess with them! -- w00t */
if ((*iter)->length != 0)
{
if ((*iter)->set_on + (*iter)->length <= ServerInstance->Time())
{
ServerInstance->Log(DEBUG, "m_svshold.so: hold on %s expired, removing...", (*iter)->nickname.c_str());
ServerInstance->WriteOpers("*** %li second SVSHOLD on %s (%s) set %u seconds ago expired", (*iter)->length, (*iter)->nickname.c_str(), (*iter)->reason.c_str(), ServerInstance->Time() - (*iter)->set_on);
HoldMap.erase(assign((*iter)->nickname));
delete *iter;
safeiter = iter;
--iter;
SVSHolds.erase(safeiter);
}
}
}
}
};
MODULE_INIT(ModuleSVSHold)
\ No newline at end of file +/* +------------------------------------+ + * | 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 <algorithm> +#include "users.h" +#include "channels.h" +#include "modules.h" +#include "configreader.h" + +/* $ModDesc: Implements SVSHOLD. Like Q:Lines, but can only be added/removed by Services. */ + +/** Holds a SVSHold item + */ +class SVSHold : public classbase +{ +public: + std::string nickname; + std::string set_by; + time_t set_on; + long length; + std::string reason; + + SVSHold() + { + } + + SVSHold(const std::string &nn, const std::string &sb, const time_t so, const long ln, const std::string &rs) : nickname(nn), set_by(sb), set_on(so), length(ln), reason(rs) + { + } +}; + + +bool SVSHoldComp(const SVSHold* ban1, const SVSHold* ban2); + +typedef std::vector<SVSHold*> SVSHoldlist; +typedef std::map<irc::string, SVSHold*> SVSHoldMap; + +/* SVSHolds is declared here, as our type is right above. Don't try move it. */ +SVSHoldlist SVSHolds; +SVSHoldMap HoldMap; + +/** Handle /SVSHold + */ +class cmd_svshold : public command_t +{ + public: + cmd_svshold(InspIRCd* Me) : command_t(Me, "SVSHOLD", 'o', 1) + { + this->source = "m_svshold.so"; + this->syntax = "<nickname> [<duration> :<reason>]"; + } + + CmdResult Handle(const char** parameters, int pcnt, userrec *user) + { + /* syntax: svshold nickname time :reason goes here */ + /* 'time' is a human-readable timestring, like 2d3h2s. */ + + if (!ServerInstance->ULine(user->server)) + { + /* don't allow SVSHOLD from non-ulined clients */ + return CMD_FAILURE; + } + + if (pcnt == 1) + { + SVSHoldMap::iterator n = HoldMap.find(parameters[0]); + if (n != HoldMap.end()) + { + /* form: svshold nickname removes a hold. */ + for (SVSHoldlist::iterator iter = SVSHolds.begin(); iter != SVSHolds.end(); iter++) + { + if (parameters[0] == assign((*iter)->nickname)) + { + unsigned long remaining = 0; + if ((*iter)->length) + { + remaining = ((*iter)->set_on + (*iter)->length) - ServerInstance->Time(); + user->WriteServ( "386 %s %s :Removed SVSHOLD with %lu seconds left before expiry (%s)", user->nick, (*iter)->nickname.c_str(), remaining, (*iter)->reason.c_str()); + } + else + { + user->WriteServ( "386 %s %s :Removed permanent SVSHOLD (%s)", user->nick, (*iter)->nickname.c_str(), (*iter)->reason.c_str()); + } + SVSHolds.erase(iter); + break; + } + } + + HoldMap.erase(n); + delete n->second; + } + } + else if (pcnt >= 2) + { + /* full form to add a SVSHold */ + if (ServerInstance->IsNick(parameters[0])) + { + // parameters[0] = w00t + // parameters[1] = 1h3m2s + // parameters[2] = Registered nickname + + /* Already exists? */ + if (HoldMap.find(parameters[0]) != HoldMap.end()) + { + user->WriteServ( "385 %s %s :SVSHOLD already exists", user->nick, parameters[0]); + return CMD_FAILURE; + } + + long length = ServerInstance->Duration(parameters[1]); + std::string reason = (pcnt > 2) ? parameters[2] : "No reason supplied"; + + SVSHold* S = new SVSHold(parameters[0], user->nick, ServerInstance->Time(), length, reason); + SVSHolds.push_back(S); + HoldMap[parameters[0]] = S; + + std::sort(SVSHolds.begin(), SVSHolds.end(), SVSHoldComp); + + if(length > 0) + { + user->WriteServ( "385 %s %s :Added %lu second SVSHOLD (%s)", user->nick, parameters[0], length, reason.c_str()); + ServerInstance->WriteOpers("*** %s added %lu second SVSHOLD on %s (%s)", user->nick, length, parameters[0], reason.c_str()); + } + else + { + user->WriteServ( "385 %s %s :Added permanent SVSHOLD on %s (%s)", user->nick, parameters[0], parameters[0], reason.c_str()); + ServerInstance->WriteOpers("*** %s added permanent SVSHOLD on %s (%s)", user->nick, parameters[0], reason.c_str()); + } + } + else + { + /* as this is primarily a Services command, do not provide an error */ + return CMD_FAILURE; + } + } + + return CMD_SUCCESS; + } +}; + +bool SVSHoldComp(const SVSHold* ban1, const SVSHold* ban2) +{ + return ((ban1->set_on + ban1->length) < (ban2->set_on + ban2->length)); +} + +class ModuleSVSHold : public Module +{ + cmd_svshold *mycommand; + + + public: + ModuleSVSHold(InspIRCd* Me) : Module(Me) + { + mycommand = new cmd_svshold(Me); + ServerInstance->AddCommand(mycommand); + } + + void Implements(char* List) + { + List[I_OnUserPreNick] = List[I_OnSyncOtherMetaData] = List[I_OnDecodeMetaData] = List[I_OnStats] = 1; + } + + virtual int OnStats(char symbol, userrec* user, string_list &results) + { + ExpireBans(); + + if(symbol == 'S') + { + for(SVSHoldlist::iterator iter = SVSHolds.begin(); iter != SVSHolds.end(); iter++) + { + unsigned long remaining = ((*iter)->set_on + (*iter)->length) - ServerInstance->Time(); + results.push_back(std::string(ServerInstance->Config->ServerName)+" 210 "+user->nick+" "+(*iter)->nickname.c_str()+" "+(*iter)->set_by+" "+ConvToStr((*iter)->set_on)+" "+ConvToStr((*iter)->length)+" "+ConvToStr(remaining)+" :"+(*iter)->reason); + } + } + + return 0; + } + + virtual int OnUserPreNick(userrec *user, const std::string &newnick) + { + ExpireBans(); + + /* check SVSHolds in here, and apply as necessary. */ + SVSHoldMap::iterator n = HoldMap.find(assign(newnick)); + if (n != HoldMap.end()) + { + user->WriteServ( "432 %s %s :Reserved nickname: %s", user->nick, newnick.c_str(), n->second->reason.c_str()); + return 1; + } + return 0; + } + + virtual void OnSyncOtherMetaData(Module* proto, void* opaque, bool displayable) + { + for(SVSHoldMap::iterator iter = HoldMap.begin(); iter != HoldMap.end(); iter++) + { + proto->ProtoSendMetaData(opaque, TYPE_OTHER, NULL, "SVSHold", EncodeSVSHold(iter->second)); + } + } + + virtual void OnDecodeMetaData(int target_type, void* target, const std::string &extname, const std::string &extdata) + { + if((target_type == TYPE_OTHER) && (extname == "SVSHold")) + { + SVSHold* S = DecodeSVSHold(extdata); /* NOTE: Allocates a new SVSHold* */ + if (HoldMap.find(assign(S->nickname)) == HoldMap.end()) + { + SVSHolds.push_back(S); + HoldMap[assign(S->nickname)] = S; + std::sort(SVSHolds.begin(), SVSHolds.end(), SVSHoldComp); + } + else + { + delete S; + } + } + } + + virtual ~ModuleSVSHold() + { + } + + virtual Version GetVersion() + { + return Version(1,1,0,1,VF_VENDOR|VF_COMMON,API_VERSION); + } + + std::string EncodeSVSHold(const SVSHold* ban) + { + std::ostringstream stream; + stream << ban->nickname << " " << ban->set_by << " " << ban->set_on << " " << ban->length << " :" << ban->reason; + return stream.str(); + } + + SVSHold* DecodeSVSHold(const std::string &data) + { + SVSHold* res = new SVSHold(); + int set_on; + irc::tokenstream tokens(data); + tokens.GetToken(res->nickname); + tokens.GetToken(res->set_by); + tokens.GetToken(set_on); + res->set_on = set_on; + tokens.GetToken(res->length); + tokens.GetToken(res->reason); + return res; + } + + void ExpireBans() + { + SVSHoldlist::iterator iter,safeiter; + for (iter = SVSHolds.begin(); iter != SVSHolds.end(); iter++) + { + /* 0 == permanent, don't mess with them! -- w00t */ + if ((*iter)->length != 0) + { + if ((*iter)->set_on + (*iter)->length <= ServerInstance->Time()) + { + ServerInstance->Log(DEBUG, "m_svshold.so: hold on %s expired, removing...", (*iter)->nickname.c_str()); + ServerInstance->WriteOpers("*** %li second SVSHOLD on %s (%s) set %u seconds ago expired", (*iter)->length, (*iter)->nickname.c_str(), (*iter)->reason.c_str(), ServerInstance->Time() - (*iter)->set_on); + HoldMap.erase(assign((*iter)->nickname)); + delete *iter; + safeiter = iter; + --iter; + SVSHolds.erase(safeiter); + } + } + } + } +}; + +MODULE_INIT(ModuleSVSHold) diff --git a/src/modules/m_swhois.cpp b/src/modules/m_swhois.cpp index 5df5fe4eb..d635654a5 100644 --- a/src/modules/m_swhois.cpp +++ b/src/modules/m_swhois.cpp @@ -1 +1,267 @@ -/* +------------------------------------+
* | 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 "users.h"
#include "channels.h"
#include "modules.h"
/* $ModDesc: Provides the SWHOIS command which allows setting of arbitary WHOIS lines */
/** Handle /SWHOIS
*/
class cmd_swhois : public command_t
{
public:
cmd_swhois (InspIRCd* Instance) : command_t(Instance,"SWHOIS",'o',2)
{
this->source = "m_swhois.so";
syntax = "<nick> <swhois>";
}
CmdResult Handle(const char** parameters, int pcnt, userrec* user)
{
userrec* dest = ServerInstance->FindNick(parameters[0]);
if (!dest)
{
user->WriteServ("401 %s %s :No such nick/channel", user->nick, parameters[0]);
return CMD_FAILURE;
}
if (!*parameters[1])
{
user->WriteServ("NOTICE %s :*** SWHOIS: Whois line must be specified", user->nick);
return CMD_FAILURE;
}
std::string line;
for (int i = 1; i < pcnt; i++)
{
if (i != 1)
line.append(" ");
line.append(parameters[i]);
}
std::string* text;
dest->GetExt("swhois", text);
if (text)
{
// We already had it set...
if (!ServerInstance->ULine(user->server))
// Ulines set SWHOISes silently
ServerInstance->WriteOpers("*** %s used SWHOIS to set %s's extra whois from '%s' to '%s'", user->nick, dest->nick, text->c_str(), line.c_str());
dest->Shrink("swhois");
DELETE(text);
}
else if (!ServerInstance->ULine(user->server))
{
// Ulines set SWHOISes silently
ServerInstance->WriteOpers("*** %s used SWHOIS to set %s's extra whois to '%s'", user->nick, dest->nick, line.c_str());
}
text = new std::string(line);
dest->Extend("swhois", text);
return CMD_SUCCESS;
}
};
class ModuleSWhois : public Module
{
cmd_swhois* mycommand;
ConfigReader* Conf;
public:
ModuleSWhois(InspIRCd* Me) : Module(Me)
{
Conf = new ConfigReader(ServerInstance);
mycommand = new cmd_swhois(ServerInstance);
ServerInstance->AddCommand(mycommand);
}
void OnRehash(userrec* user, const std::string ¶meter)
{
DELETE(Conf);
Conf = new ConfigReader(ServerInstance);
}
void Implements(char* List)
{
List[I_OnDecodeMetaData] = List[I_OnWhoisLine] = List[I_OnSyncUserMetaData] = List[I_OnUserQuit] = List[I_OnCleanup] = List[I_OnRehash] = List[I_OnPostCommand] = 1;
}
// :kenny.chatspike.net 320 Brain Azhrarn :is getting paid to play games.
int OnWhoisLine(userrec* user, userrec* dest, int &numeric, std::string &text)
{
/* We use this and not OnWhois because this triggers for remote, too */
if (numeric == 312)
{
/* Insert our numeric before 312 */
std::string* swhois;
dest->GetExt("swhois", swhois);
if (swhois)
{
ServerInstance->SendWhoisLine(user, dest, 320, "%s %s :%s",user->nick,dest->nick,swhois->c_str());
}
}
/* Dont block anything */
return 0;
}
// Whenever the linking module wants to send out data, but doesnt know what the data
// represents (e.g. it is metadata, added to a userrec or chanrec by a module) then
// this method is called. We should use the ProtoSendMetaData function after we've
// corrected decided how the data should look, to send the metadata on its way if
// it is ours.
virtual void OnSyncUserMetaData(userrec* user, Module* proto, void* opaque, const std::string &extname, bool displayable)
{
// check if the linking module wants to know about OUR metadata
if (extname == "swhois")
{
// check if this user has an swhois field to send
std::string* swhois;
user->GetExt("swhois", swhois);
if (swhois)
{
// call this function in the linking module, let it format the data how it
// sees fit, and send it on its way. We dont need or want to know how.
proto->ProtoSendMetaData(opaque,TYPE_USER,user,extname,*swhois);
}
}
}
// when a user quits, tidy up their metadata
virtual void OnUserQuit(userrec* user, const std::string &message, const std::string &oper_message)
{
std::string* swhois;
user->GetExt("swhois", swhois);
if (swhois)
{
user->Shrink("swhois");
DELETE(swhois);
}
}
// if the module is unloaded, tidy up all our dangling metadata
virtual void OnCleanup(int target_type, void* item)
{
if (target_type == TYPE_USER)
{
userrec* user = (userrec*)item;
std::string* swhois;
user->GetExt("swhois", swhois);
if (swhois)
{
user->Shrink("swhois");
DELETE(swhois);
}
}
}
// Whenever the linking module receives metadata from another server and doesnt know what
// to do with it (of course, hence the 'meta') it calls this method, and it is up to each
// module in turn to figure out if this metadata key belongs to them, and what they want
// to do with it.
// In our case we're only sending a single string around, so we just construct a std::string.
// Some modules will probably get much more complex and format more detailed structs and classes
// in a textual way for sending over the link.
virtual void OnDecodeMetaData(int target_type, void* target, const std::string &extname, const std::string &extdata)
{
// check if its our metadata key, and its associated with a user
if ((target_type == TYPE_USER) && (extname == "swhois"))
{
userrec* dest = (userrec*)target;
// if they dont already have an swhois field, accept the remote server's
std::string* text;
if (!dest->GetExt("swhois", text))
{
std::string* text = new std::string(extdata);
dest->Extend("swhois",text);
}
}
}
virtual void OnPostCommand(const std::string &command, const char **params, int pcnt, userrec *user, CmdResult result, const std::string &original_line)
{
if ((command != "OPER") || (result != CMD_SUCCESS))
return;
std::string swhois;
for (int i = 0; i < Conf->Enumerate("oper"); i++)
{
std::string name = Conf->ReadValue("oper", "name", i);
if (name == params[0])
{
swhois = Conf->ReadValue("oper", "swhois", i);
break;
}
}
if (!swhois.length())
{
for (int i = 0; i < Conf->Enumerate("type"); i++)
{
std::string type = Conf->ReadValue("type", "name", i);
if (type == user->oper)
{
swhois = Conf->ReadValue("type", "swhois", i);
break;
}
}
}
std::string *old;
if (user->GetExt("swhois", old))
{
user->Shrink("swhois");
DELETE(old);
}
if (!swhois.length())
return;
std::string *text = new std::string(swhois);
user->Extend("swhois", text);
std::deque<std::string>* metadata = new std::deque<std::string>;
metadata->push_back(user->nick);
metadata->push_back("swhois"); // The metadata id
metadata->push_back(*text); // The value to send
Event event((char*)metadata,(Module*)this,"send_metadata");
event.Send(ServerInstance);
delete metadata;
}
virtual ~ModuleSWhois()
{
DELETE(Conf);
}
virtual Version GetVersion()
{
return Version(1,1,0,0,VF_VENDOR,API_VERSION);
}
};
MODULE_INIT(ModuleSWhois)
\ No newline at end of file +/* +------------------------------------+ + * | 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 "users.h" +#include "channels.h" +#include "modules.h" + +/* $ModDesc: Provides the SWHOIS command which allows setting of arbitary WHOIS lines */ + +/** Handle /SWHOIS + */ +class cmd_swhois : public command_t +{ + + public: + cmd_swhois (InspIRCd* Instance) : command_t(Instance,"SWHOIS",'o',2) + { + this->source = "m_swhois.so"; + syntax = "<nick> <swhois>"; + } + + CmdResult Handle(const char** parameters, int pcnt, userrec* user) + { + userrec* dest = ServerInstance->FindNick(parameters[0]); + + if (!dest) + { + user->WriteServ("401 %s %s :No such nick/channel", user->nick, parameters[0]); + return CMD_FAILURE; + } + + if (!*parameters[1]) + { + user->WriteServ("NOTICE %s :*** SWHOIS: Whois line must be specified", user->nick); + return CMD_FAILURE; + } + + std::string line; + for (int i = 1; i < pcnt; i++) + { + if (i != 1) + line.append(" "); + + line.append(parameters[i]); + } + + std::string* text; + dest->GetExt("swhois", text); + + if (text) + { + // We already had it set... + + if (!ServerInstance->ULine(user->server)) + // Ulines set SWHOISes silently + ServerInstance->WriteOpers("*** %s used SWHOIS to set %s's extra whois from '%s' to '%s'", user->nick, dest->nick, text->c_str(), line.c_str()); + + dest->Shrink("swhois"); + DELETE(text); + } + else if (!ServerInstance->ULine(user->server)) + { + // Ulines set SWHOISes silently + ServerInstance->WriteOpers("*** %s used SWHOIS to set %s's extra whois to '%s'", user->nick, dest->nick, line.c_str()); + } + + text = new std::string(line); + dest->Extend("swhois", text); + + return CMD_SUCCESS; + } + +}; + +class ModuleSWhois : public Module +{ + cmd_swhois* mycommand; + + ConfigReader* Conf; + + public: + ModuleSWhois(InspIRCd* Me) : Module(Me) + { + + Conf = new ConfigReader(ServerInstance); + mycommand = new cmd_swhois(ServerInstance); + ServerInstance->AddCommand(mycommand); + } + + void OnRehash(userrec* user, const std::string ¶meter) + { + DELETE(Conf); + Conf = new ConfigReader(ServerInstance); + } + + void Implements(char* List) + { + List[I_OnDecodeMetaData] = List[I_OnWhoisLine] = List[I_OnSyncUserMetaData] = List[I_OnUserQuit] = List[I_OnCleanup] = List[I_OnRehash] = List[I_OnPostCommand] = 1; + } + + // :kenny.chatspike.net 320 Brain Azhrarn :is getting paid to play games. + int OnWhoisLine(userrec* user, userrec* dest, int &numeric, std::string &text) + { + /* We use this and not OnWhois because this triggers for remote, too */ + if (numeric == 312) + { + /* Insert our numeric before 312 */ + std::string* swhois; + dest->GetExt("swhois", swhois); + if (swhois) + { + ServerInstance->SendWhoisLine(user, dest, 320, "%s %s :%s",user->nick,dest->nick,swhois->c_str()); + } + } + /* Dont block anything */ + return 0; + } + + // Whenever the linking module wants to send out data, but doesnt know what the data + // represents (e.g. it is metadata, added to a userrec or chanrec by a module) then + // this method is called. We should use the ProtoSendMetaData function after we've + // corrected decided how the data should look, to send the metadata on its way if + // it is ours. + virtual void OnSyncUserMetaData(userrec* user, Module* proto, void* opaque, const std::string &extname, bool displayable) + { + // check if the linking module wants to know about OUR metadata + if (extname == "swhois") + { + // check if this user has an swhois field to send + std::string* swhois; + user->GetExt("swhois", swhois); + if (swhois) + { + // call this function in the linking module, let it format the data how it + // sees fit, and send it on its way. We dont need or want to know how. + proto->ProtoSendMetaData(opaque,TYPE_USER,user,extname,*swhois); + } + } + } + + // when a user quits, tidy up their metadata + virtual void OnUserQuit(userrec* user, const std::string &message, const std::string &oper_message) + { + std::string* swhois; + user->GetExt("swhois", swhois); + if (swhois) + { + user->Shrink("swhois"); + DELETE(swhois); + } + } + + // if the module is unloaded, tidy up all our dangling metadata + virtual void OnCleanup(int target_type, void* item) + { + if (target_type == TYPE_USER) + { + userrec* user = (userrec*)item; + std::string* swhois; + user->GetExt("swhois", swhois); + if (swhois) + { + user->Shrink("swhois"); + DELETE(swhois); + } + } + } + + // Whenever the linking module receives metadata from another server and doesnt know what + // to do with it (of course, hence the 'meta') it calls this method, and it is up to each + // module in turn to figure out if this metadata key belongs to them, and what they want + // to do with it. + // In our case we're only sending a single string around, so we just construct a std::string. + // Some modules will probably get much more complex and format more detailed structs and classes + // in a textual way for sending over the link. + virtual void OnDecodeMetaData(int target_type, void* target, const std::string &extname, const std::string &extdata) + { + // check if its our metadata key, and its associated with a user + if ((target_type == TYPE_USER) && (extname == "swhois")) + { + userrec* dest = (userrec*)target; + // if they dont already have an swhois field, accept the remote server's + std::string* text; + if (!dest->GetExt("swhois", text)) + { + std::string* text = new std::string(extdata); + dest->Extend("swhois",text); + } + } + } + + virtual void OnPostCommand(const std::string &command, const char **params, int pcnt, userrec *user, CmdResult result, const std::string &original_line) + { + if ((command != "OPER") || (result != CMD_SUCCESS)) + return; + + std::string swhois; + + for (int i = 0; i < Conf->Enumerate("oper"); i++) + { + std::string name = Conf->ReadValue("oper", "name", i); + + if (name == params[0]) + { + swhois = Conf->ReadValue("oper", "swhois", i); + break; + } + } + + if (!swhois.length()) + { + for (int i = 0; i < Conf->Enumerate("type"); i++) + { + std::string type = Conf->ReadValue("type", "name", i); + + if (type == user->oper) + { + swhois = Conf->ReadValue("type", "swhois", i); + break; + } + } + } + + std::string *old; + if (user->GetExt("swhois", old)) + { + user->Shrink("swhois"); + DELETE(old); + } + + if (!swhois.length()) + return; + + std::string *text = new std::string(swhois); + user->Extend("swhois", text); + std::deque<std::string>* metadata = new std::deque<std::string>; + metadata->push_back(user->nick); + metadata->push_back("swhois"); // The metadata id + metadata->push_back(*text); // The value to send + Event event((char*)metadata,(Module*)this,"send_metadata"); + event.Send(ServerInstance); + delete metadata; + } + + virtual ~ModuleSWhois() + { + DELETE(Conf); + } + + virtual Version GetVersion() + { + return Version(1,1,0,0,VF_VENDOR,API_VERSION); + } +}; + +MODULE_INIT(ModuleSWhois) diff --git a/src/modules/m_taxonomy.cpp b/src/modules/m_taxonomy.cpp index edae9ccf6..79dc8e23f 100644 --- a/src/modules/m_taxonomy.cpp +++ b/src/modules/m_taxonomy.cpp @@ -1 +1,99 @@ -/* +------------------------------------+
* | 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 "users.h"
#include "channels.h"
#include "modules.h"
/* $ModDesc: Provides the /TAXONOMY command, used to view all metadata attached to a user */
/** Handle /WOOT
*/
class cmd_taxonomy : public command_t
{
Module* Creator;
bool& claimed;
public:
/* Command 'taxonomy', takes no parameters and needs no special modes */
cmd_taxonomy (InspIRCd* Instance, Module* maker, bool &claim) : command_t(Instance,"TAXONOMY", 'o', 1), Creator(maker), claimed(claim)
{
this->source = "m_taxonomy.so";
}
CmdResult Handle (const char** parameters, int pcnt, userrec *user)
{
userrec* dest = ServerInstance->FindNick(parameters[0]);
if (dest)
{
std::deque<std::string> list;
list.clear();
user->GetExtList(list);
user->WriteServ("304 " + std::string(user->nick) + ":TAXONOMY ITEMS " + std::string(dest->nick) + " " +ConvToStr(list.size()));
for (unsigned int j = 0; j < list.size(); j++)
{
claimed = false;
FOREACH_MOD(I_OnSyncUserMetaData, OnSyncUserMetaData(user, Creator, dest, list[j], true));
if (!claimed)
{
user->WriteServ("304 " + std::string(user->nick) + ":TAXONOMY METADATA " + list[j] + " = <unknown>");
}
}
user->WriteServ("304 " + std::string(user->nick) + ":TAXONOMY END");
}
return CMD_FAILURE;
}
};
class ModuleTaxonomy : public Module
{
cmd_taxonomy* newcommand;
bool claimed;
public:
ModuleTaxonomy(InspIRCd* Me)
: Module(Me)
{
// Create a new command
newcommand = new cmd_taxonomy(ServerInstance, this, claimed);
ServerInstance->AddCommand(newcommand);
}
void Implements(char* List)
{
List[I_ProtoSendMetaData] = 1;
}
void ProtoSendMetaData(void* opaque, int target_type, void* target, const std::string &extname, const std::string &extdata)
{
if (target_type == TYPE_USER)
{
userrec* spool = (userrec*)opaque;
std::string taxstr = "304 " + std::string(spool->nick) + ":TAXONOMY METADATA "+extname+" = "+extdata;
spool->WriteServ(taxstr);
claimed = true;
}
}
virtual ~ModuleTaxonomy()
{
}
virtual Version GetVersion()
{
return Version(1, 1, 0, 0, VF_VENDOR, API_VERSION);
}
};
MODULE_INIT(ModuleTaxonomy)
\ No newline at end of file +/* +------------------------------------+ + * | 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 "users.h" +#include "channels.h" +#include "modules.h" + +/* $ModDesc: Provides the /TAXONOMY command, used to view all metadata attached to a user */ + +/** Handle /WOOT + */ +class cmd_taxonomy : public command_t +{ + Module* Creator; + bool& claimed; + public: + /* Command 'taxonomy', takes no parameters and needs no special modes */ + cmd_taxonomy (InspIRCd* Instance, Module* maker, bool &claim) : command_t(Instance,"TAXONOMY", 'o', 1), Creator(maker), claimed(claim) + { + this->source = "m_taxonomy.so"; + } + + CmdResult Handle (const char** parameters, int pcnt, userrec *user) + { + userrec* dest = ServerInstance->FindNick(parameters[0]); + if (dest) + { + std::deque<std::string> list; + list.clear(); + user->GetExtList(list); + user->WriteServ("304 " + std::string(user->nick) + ":TAXONOMY ITEMS " + std::string(dest->nick) + " " +ConvToStr(list.size())); + for (unsigned int j = 0; j < list.size(); j++) + { + claimed = false; + FOREACH_MOD(I_OnSyncUserMetaData, OnSyncUserMetaData(user, Creator, dest, list[j], true)); + if (!claimed) + { + user->WriteServ("304 " + std::string(user->nick) + ":TAXONOMY METADATA " + list[j] + " = <unknown>"); + } + } + user->WriteServ("304 " + std::string(user->nick) + ":TAXONOMY END"); + } + return CMD_FAILURE; + } +}; + +class ModuleTaxonomy : public Module +{ + cmd_taxonomy* newcommand; + bool claimed; + public: + ModuleTaxonomy(InspIRCd* Me) + : Module(Me) + { + + // Create a new command + newcommand = new cmd_taxonomy(ServerInstance, this, claimed); + ServerInstance->AddCommand(newcommand); + } + + void Implements(char* List) + { + List[I_ProtoSendMetaData] = 1; + } + + void ProtoSendMetaData(void* opaque, int target_type, void* target, const std::string &extname, const std::string &extdata) + { + if (target_type == TYPE_USER) + { + userrec* spool = (userrec*)opaque; + std::string taxstr = "304 " + std::string(spool->nick) + ":TAXONOMY METADATA "+extname+" = "+extdata; + spool->WriteServ(taxstr); + claimed = true; + } + } + + virtual ~ModuleTaxonomy() + { + } + + virtual Version GetVersion() + { + return Version(1, 1, 0, 0, VF_VENDOR, API_VERSION); + } +}; + +MODULE_INIT(ModuleTaxonomy) + diff --git a/src/modules/m_testcommand.cpp b/src/modules/m_testcommand.cpp index 0733fd0f0..6ec197eb6 100644 --- a/src/modules/m_testcommand.cpp +++ b/src/modules/m_testcommand.cpp @@ -1 +1,67 @@ -/* +------------------------------------+
* | 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 "users.h"
#include "channels.h"
#include "modules.h"
/* $ModDesc: Provides a pointless /dalinfo command, demo module */
/** Handle /DALINFO
*/
class cmd_dalinfo : public command_t
{
public:
/* Command 'dalinfo', takes no parameters and needs no special modes */
cmd_dalinfo (InspIRCd* Instance) : command_t(Instance,"DALINFO", 0, 0)
{
this->source = "m_testcommand.so";
}
CmdResult Handle (const char** parameters, int pcnt, userrec *user)
{
user->WriteServ("NOTICE %s :*** DALNet had nothing to do with it.", user->nick);
return CMD_FAILURE;
}
};
class ModuleTestCommand : public Module
{
cmd_dalinfo* newcommand;
public:
ModuleTestCommand(InspIRCd* Me)
: Module(Me)
{
// Create a new command
newcommand = new cmd_dalinfo(ServerInstance);
ServerInstance->AddCommand(newcommand);
}
void Implements(char* List)
{
}
virtual ~ModuleTestCommand()
{
delete newcommand;
}
virtual Version GetVersion()
{
return Version(1, 1, 0, 0, VF_VENDOR, API_VERSION);
}
};
MODULE_INIT(ModuleTestCommand)
\ No newline at end of file +/* +------------------------------------+ + * | 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 "users.h" +#include "channels.h" +#include "modules.h" + +/* $ModDesc: Provides a pointless /dalinfo command, demo module */ + +/** Handle /DALINFO + */ +class cmd_dalinfo : public command_t +{ + public: + /* Command 'dalinfo', takes no parameters and needs no special modes */ + cmd_dalinfo (InspIRCd* Instance) : command_t(Instance,"DALINFO", 0, 0) + { + this->source = "m_testcommand.so"; + } + + CmdResult Handle (const char** parameters, int pcnt, userrec *user) + { + user->WriteServ("NOTICE %s :*** DALNet had nothing to do with it.", user->nick); + return CMD_FAILURE; + } +}; + +class ModuleTestCommand : public Module +{ + cmd_dalinfo* newcommand; + public: + ModuleTestCommand(InspIRCd* Me) + : Module(Me) + { + // Create a new command + newcommand = new cmd_dalinfo(ServerInstance); + ServerInstance->AddCommand(newcommand); + } + + void Implements(char* List) + { + } + + virtual ~ModuleTestCommand() + { + delete newcommand; + } + + virtual Version GetVersion() + { + return Version(1, 1, 0, 0, VF_VENDOR, API_VERSION); + } +}; + +MODULE_INIT(ModuleTestCommand) + diff --git a/src/modules/m_timedbans.cpp b/src/modules/m_timedbans.cpp index f705a1f95..ae3da7549 100644 --- a/src/modules/m_timedbans.cpp +++ b/src/modules/m_timedbans.cpp @@ -1 +1,204 @@ -/* +------------------------------------+
* | 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.
*
* ---------------------------------------------------
*/
/* $ModDesc: Adds timed bans */
#include "inspircd.h"
#include "users.h"
#include "channels.h"
#include "modules.h"
#include "hashcomp.h"
#include "configreader.h"
/** Holds a timed ban
*/
class TimedBan : public classbase
{
public:
std::string channel;
std::string mask;
time_t expire;
};
typedef std::vector<TimedBan> timedbans;
timedbans TimedBanList;
/** Handle /TBAN
*/
class cmd_tban : public command_t
{
public:
cmd_tban (InspIRCd* Instance) : command_t(Instance,"TBAN", 0, 3)
{
this->source = "m_timedbans.so";
syntax = "<channel> <duration> <banmask>";
}
CmdResult Handle (const char** parameters, int pcnt, userrec *user)
{
chanrec* channel = ServerInstance->FindChan(parameters[0]);
if (channel)
{
int cm = channel->GetStatus(user);
if ((cm == STATUS_HOP) || (cm == STATUS_OP))
{
if (!ServerInstance->IsValidMask(parameters[2]))
{
user->WriteServ("NOTICE "+std::string(user->nick)+" :Invalid ban mask");
return CMD_FAILURE;
}
for (BanList::iterator i = channel->bans.begin(); i != channel->bans.end(); i++)
{
if (!strcasecmp(i->data,parameters[2]))
{
user->WriteServ("NOTICE "+std::string(user->nick)+" :The ban "+std::string(parameters[2])+" is already on the banlist of "+std::string(parameters[0]));
return CMD_FAILURE;
}
}
TimedBan T;
std::string channelname = parameters[0];
long duration = ServerInstance->Duration(parameters[1]);
unsigned long expire = duration + time(NULL);
if (duration < 1)
{
user->WriteServ("NOTICE "+std::string(user->nick)+" :Invalid ban time");
return CMD_FAILURE;
}
std::string mask = parameters[2];
const char *setban[32];
setban[0] = parameters[0];
setban[1] = "+b";
setban[2] = parameters[2];
// use CallCommandHandler to make it so that the user sets the mode
// themselves
ServerInstance->CallCommandHandler("MODE",setban,3,user);
/* Check if the ban was actually added (e.g. banlist was NOT full) */
bool was_added = false;
for (BanList::iterator i = channel->bans.begin(); i != channel->bans.end(); i++)
if (!strcasecmp(i->data,mask.c_str()))
was_added = true;
if (was_added)
{
T.channel = channelname;
T.mask = mask;
T.expire = expire;
TimedBanList.push_back(T);
channel->WriteChannelWithServ(ServerInstance->Config->ServerName, "NOTICE %s :%s added a timed ban on %s lasting for %ld seconds.", channel->name, user->nick, mask.c_str(), duration);
return CMD_SUCCESS;
}
return CMD_FAILURE;
}
else user->WriteServ("482 %s %s :You must be at least a half-operator to change modes on this channel",user->nick, channel->name);
return CMD_FAILURE;
}
user->WriteServ("401 %s %s :No such channel",user->nick, parameters[0]);
return CMD_FAILURE;
}
};
class ModuleTimedBans : public Module
{
cmd_tban* mycommand;
public:
ModuleTimedBans(InspIRCd* Me)
: Module(Me)
{
mycommand = new cmd_tban(ServerInstance);
ServerInstance->AddCommand(mycommand);
TimedBanList.clear();
}
virtual ~ModuleTimedBans()
{
TimedBanList.clear();
}
void Implements(char* List)
{
List[I_OnDelBan] = List[I_OnBackgroundTimer] = 1;
}
virtual int OnDelBan(userrec* source, chanrec* chan, const std::string &banmask)
{
irc::string listitem = banmask.c_str();
irc::string thischan = chan->name;
for (timedbans::iterator i = TimedBanList.begin(); i < TimedBanList.end(); i++)
{
irc::string target = i->mask.c_str();
irc::string tchan = i->channel.c_str();
if ((listitem == target) && (tchan == thischan))
{
TimedBanList.erase(i);
break;
}
}
return 0;
}
virtual void OnBackgroundTimer(time_t curtime)
{
bool again = true;
while (again)
{
again = false;
for (timedbans::iterator i = TimedBanList.begin(); i < TimedBanList.end(); i++)
{
if (curtime > i->expire)
{
chanrec* cr = ServerInstance->FindChan(i->channel);
again = true;
if (cr)
{
cr->WriteChannelWithServ(ServerInstance->Config->ServerName, "NOTICE %s :Timed ban on %s expired.", cr->name, i->mask.c_str());
const char *setban[3];
setban[0] = i->channel.c_str();
setban[1] = "-b";
setban[2] = i->mask.c_str();
// kludge alert!
// ::SendMode expects a userrec* to send the numeric replies
// back to, so we create it a fake user that isnt in the user
// hash and set its descriptor to FD_MAGIC_NUMBER so the data
// falls into the abyss :p
userrec* temp = new userrec(ServerInstance);
temp->SetFd(FD_MAGIC_NUMBER);
/* FIX: Send mode remotely*/
std::deque<std::string> n;
n.push_back(setban[0]);
n.push_back("-b");
n.push_back(setban[2]);
ServerInstance->SendMode(setban,3,temp);
Event rmode((char *)&n, NULL, "send_mode");
rmode.Send(ServerInstance);
DELETE(temp);
}
else
{
/* Where the hell did our channel go?! */
TimedBanList.erase(i);
}
// we used to delete the item here, but we dont need to as the servermode above does it for us,
break;
}
}
}
}
virtual Version GetVersion()
{
return Version(1,1,0,0,VF_VENDOR,API_VERSION);
}
};
MODULE_INIT(ModuleTimedBans)
\ No newline at end of file +/* +------------------------------------+ + * | 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. + * + * --------------------------------------------------- + */ + +/* $ModDesc: Adds timed bans */ + +#include "inspircd.h" +#include "users.h" +#include "channels.h" +#include "modules.h" +#include "hashcomp.h" +#include "configreader.h" + +/** Holds a timed ban + */ +class TimedBan : public classbase +{ + public: + std::string channel; + std::string mask; + time_t expire; +}; + +typedef std::vector<TimedBan> timedbans; +timedbans TimedBanList; + +/** Handle /TBAN + */ +class cmd_tban : public command_t +{ + public: + cmd_tban (InspIRCd* Instance) : command_t(Instance,"TBAN", 0, 3) + { + this->source = "m_timedbans.so"; + syntax = "<channel> <duration> <banmask>"; + } + + CmdResult Handle (const char** parameters, int pcnt, userrec *user) + { + chanrec* channel = ServerInstance->FindChan(parameters[0]); + if (channel) + { + int cm = channel->GetStatus(user); + if ((cm == STATUS_HOP) || (cm == STATUS_OP)) + { + if (!ServerInstance->IsValidMask(parameters[2])) + { + user->WriteServ("NOTICE "+std::string(user->nick)+" :Invalid ban mask"); + return CMD_FAILURE; + } + for (BanList::iterator i = channel->bans.begin(); i != channel->bans.end(); i++) + { + if (!strcasecmp(i->data,parameters[2])) + { + user->WriteServ("NOTICE "+std::string(user->nick)+" :The ban "+std::string(parameters[2])+" is already on the banlist of "+std::string(parameters[0])); + return CMD_FAILURE; + } + } + TimedBan T; + std::string channelname = parameters[0]; + long duration = ServerInstance->Duration(parameters[1]); + unsigned long expire = duration + time(NULL); + if (duration < 1) + { + user->WriteServ("NOTICE "+std::string(user->nick)+" :Invalid ban time"); + return CMD_FAILURE; + } + std::string mask = parameters[2]; + const char *setban[32]; + setban[0] = parameters[0]; + setban[1] = "+b"; + setban[2] = parameters[2]; + // use CallCommandHandler to make it so that the user sets the mode + // themselves + ServerInstance->CallCommandHandler("MODE",setban,3,user); + /* Check if the ban was actually added (e.g. banlist was NOT full) */ + bool was_added = false; + for (BanList::iterator i = channel->bans.begin(); i != channel->bans.end(); i++) + if (!strcasecmp(i->data,mask.c_str())) + was_added = true; + if (was_added) + { + T.channel = channelname; + T.mask = mask; + T.expire = expire; + TimedBanList.push_back(T); + channel->WriteChannelWithServ(ServerInstance->Config->ServerName, "NOTICE %s :%s added a timed ban on %s lasting for %ld seconds.", channel->name, user->nick, mask.c_str(), duration); + return CMD_SUCCESS; + } + return CMD_FAILURE; + } + else user->WriteServ("482 %s %s :You must be at least a half-operator to change modes on this channel",user->nick, channel->name); + return CMD_FAILURE; + } + user->WriteServ("401 %s %s :No such channel",user->nick, parameters[0]); + return CMD_FAILURE; + } +}; + +class ModuleTimedBans : public Module +{ + cmd_tban* mycommand; + public: + ModuleTimedBans(InspIRCd* Me) + : Module(Me) + { + + mycommand = new cmd_tban(ServerInstance); + ServerInstance->AddCommand(mycommand); + TimedBanList.clear(); + } + + virtual ~ModuleTimedBans() + { + TimedBanList.clear(); + } + + void Implements(char* List) + { + List[I_OnDelBan] = List[I_OnBackgroundTimer] = 1; + } + + virtual int OnDelBan(userrec* source, chanrec* chan, const std::string &banmask) + { + irc::string listitem = banmask.c_str(); + irc::string thischan = chan->name; + for (timedbans::iterator i = TimedBanList.begin(); i < TimedBanList.end(); i++) + { + irc::string target = i->mask.c_str(); + irc::string tchan = i->channel.c_str(); + if ((listitem == target) && (tchan == thischan)) + { + TimedBanList.erase(i); + break; + } + } + return 0; + } + + virtual void OnBackgroundTimer(time_t curtime) + { + bool again = true; + while (again) + { + again = false; + for (timedbans::iterator i = TimedBanList.begin(); i < TimedBanList.end(); i++) + { + if (curtime > i->expire) + { + chanrec* cr = ServerInstance->FindChan(i->channel); + again = true; + if (cr) + { + cr->WriteChannelWithServ(ServerInstance->Config->ServerName, "NOTICE %s :Timed ban on %s expired.", cr->name, i->mask.c_str()); + const char *setban[3]; + setban[0] = i->channel.c_str(); + setban[1] = "-b"; + setban[2] = i->mask.c_str(); + // kludge alert! + // ::SendMode expects a userrec* to send the numeric replies + // back to, so we create it a fake user that isnt in the user + // hash and set its descriptor to FD_MAGIC_NUMBER so the data + // falls into the abyss :p + userrec* temp = new userrec(ServerInstance); + temp->SetFd(FD_MAGIC_NUMBER); + /* FIX: Send mode remotely*/ + std::deque<std::string> n; + n.push_back(setban[0]); + n.push_back("-b"); + n.push_back(setban[2]); + ServerInstance->SendMode(setban,3,temp); + Event rmode((char *)&n, NULL, "send_mode"); + rmode.Send(ServerInstance); + DELETE(temp); + } + else + { + /* Where the hell did our channel go?! */ + TimedBanList.erase(i); + } + // we used to delete the item here, but we dont need to as the servermode above does it for us, + break; + } + } + } + } + + virtual Version GetVersion() + { + return Version(1,1,0,0,VF_VENDOR,API_VERSION); + } +}; + +MODULE_INIT(ModuleTimedBans) + diff --git a/src/modules/m_tline.cpp b/src/modules/m_tline.cpp index dd13a965c..834cb7f4c 100644 --- a/src/modules/m_tline.cpp +++ b/src/modules/m_tline.cpp @@ -1 +1,95 @@ -/* +------------------------------------+
* | 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 "users.h"
#include "channels.h"
#include "modules.h"
#include "wildcard.h"
/* $ModDesc: Provides /tline command used to test who a mask matches */
/** Handle /TLINE
*/
class cmd_tline : public command_t
{
public:
cmd_tline (InspIRCd* Instance) : command_t(Instance,"TLINE", 'o', 1)
{
this->source = "m_tline.so";
this->syntax = "<mask>";
}
CmdResult Handle (const char** parameters, int pcnt, userrec *user)
{
float n_counted = 0;
float n_matched = 0;
float n_match_host = 0;
float n_match_ip = 0;
for (user_hash::const_iterator u = ServerInstance->clientlist->begin(); u != ServerInstance->clientlist->end(); u++)
{
n_counted++;
if (match(u->second->GetFullRealHost(),parameters[0]))
{
n_matched++;
n_match_host++;
}
else
{
char host[MAXBUF];
snprintf(host, MAXBUF, "%s@%s", u->second->ident, u->second->GetIPString());
if (match(host, parameters[0], true))
{
n_matched++;
n_match_ip++;
}
}
}
if (n_matched)
user->WriteServ( "NOTICE %s :*** TLINE: Counted %0.0f user(s). Matched '%s' against %0.0f user(s) (%0.2f%% of the userbase). %0.0f by hostname and %0.0f by IP address.",user->nick, n_counted, parameters[0], n_matched, (n_matched/n_counted)*100, n_match_host, n_match_ip);
else
user->WriteServ( "NOTICE %s :*** TLINE: Counted %0.0f user(s). Matched '%s' against no user(s).", user->nick, n_counted, parameters[0]);
return CMD_LOCALONLY;
}
};
class ModuleTLine : public Module
{
cmd_tline* newcommand;
public:
ModuleTLine(InspIRCd* Me)
: Module(Me)
{
newcommand = new cmd_tline(ServerInstance);
ServerInstance->AddCommand(newcommand);
}
void Implements(char* List)
{
}
virtual ~ModuleTLine()
{
}
virtual Version GetVersion()
{
return Version(1, 1, 0, 0, VF_VENDOR,API_VERSION);
}
};
MODULE_INIT(ModuleTLine)
\ No newline at end of file +/* +------------------------------------+ + * | 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 "users.h" +#include "channels.h" +#include "modules.h" +#include "wildcard.h" + +/* $ModDesc: Provides /tline command used to test who a mask matches */ + +/** Handle /TLINE + */ +class cmd_tline : public command_t +{ + public: + cmd_tline (InspIRCd* Instance) : command_t(Instance,"TLINE", 'o', 1) + { + this->source = "m_tline.so"; + this->syntax = "<mask>"; + } + + CmdResult Handle (const char** parameters, int pcnt, userrec *user) + { + float n_counted = 0; + float n_matched = 0; + float n_match_host = 0; + float n_match_ip = 0; + + for (user_hash::const_iterator u = ServerInstance->clientlist->begin(); u != ServerInstance->clientlist->end(); u++) + { + n_counted++; + if (match(u->second->GetFullRealHost(),parameters[0])) + { + n_matched++; + n_match_host++; + } + else + { + char host[MAXBUF]; + snprintf(host, MAXBUF, "%s@%s", u->second->ident, u->second->GetIPString()); + if (match(host, parameters[0], true)) + { + n_matched++; + n_match_ip++; + } + } + } + if (n_matched) + user->WriteServ( "NOTICE %s :*** TLINE: Counted %0.0f user(s). Matched '%s' against %0.0f user(s) (%0.2f%% of the userbase). %0.0f by hostname and %0.0f by IP address.",user->nick, n_counted, parameters[0], n_matched, (n_matched/n_counted)*100, n_match_host, n_match_ip); + else + user->WriteServ( "NOTICE %s :*** TLINE: Counted %0.0f user(s). Matched '%s' against no user(s).", user->nick, n_counted, parameters[0]); + + return CMD_LOCALONLY; + } +}; + +class ModuleTLine : public Module +{ + cmd_tline* newcommand; + public: + ModuleTLine(InspIRCd* Me) + : Module(Me) + { + + newcommand = new cmd_tline(ServerInstance); + ServerInstance->AddCommand(newcommand); + } + + void Implements(char* List) + { + } + + virtual ~ModuleTLine() + { + } + + virtual Version GetVersion() + { + return Version(1, 1, 0, 0, VF_VENDOR,API_VERSION); + } +}; + +MODULE_INIT(ModuleTLine) + diff --git a/src/modules/m_uhnames.cpp b/src/modules/m_uhnames.cpp index 61b58f302..6794f4643 100644 --- a/src/modules/m_uhnames.cpp +++ b/src/modules/m_uhnames.cpp @@ -1 +1,98 @@ -/* +------------------------------------+
* | 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 "users.h"
#include "channels.h"
#include "modules.h"
static const char* dummy = "ON";
/* $ModDesc: Provides aliases of commands. */
class ModuleUHNames : public Module
{
CUList nl;
public:
ModuleUHNames(InspIRCd* Me)
: Module(Me)
{
}
void Implements(char* List)
{
List[I_OnSyncUserMetaData] = List[I_OnPreCommand] = List[I_OnUserList] = List[I_On005Numeric] = 1;
}
virtual ~ModuleUHNames()
{
}
void OnSyncUserMetaData(userrec* user, Module* proto,void* opaque, const std::string &extname, bool displayable)
{
if ((displayable) && (extname == "UHNAMES"))
proto->ProtoSendMetaData(opaque, TYPE_USER, user, extname, "Enabled");
}
virtual Version GetVersion()
{
return Version(1,1,0,1,VF_VENDOR,API_VERSION);
}
virtual void On005Numeric(std::string &output)
{
output.append(" UHNAMES");
}
Priority Prioritize()
{
return (Priority)ServerInstance->PriorityBefore("m_namesx.so");
}
virtual int OnPreCommand(const std::string &command, const char** parameters, int pcnt, userrec *user, bool validated, const std::string &original_line)
{
irc::string c = command.c_str();
/* We don't actually create a proper command handler class for PROTOCTL,
* because other modules might want to have PROTOCTL hooks too.
* Therefore, we just hook its as an unvalidated command therefore we
* can capture it even if it doesnt exist! :-)
*/
if (c == "PROTOCTL")
{
if ((pcnt) && (!strcasecmp(parameters[0],"UHNAMES")))
{
user->Extend("UHNAMES",dummy);
return 1;
}
}
return 0;
}
/* IMPORTANT: This must be prioritized above NAMESX! */
virtual int OnUserList(userrec* user, chanrec* Ptr, CUList* &ulist)
{
if (user->GetExt("UHNAMES"))
{
if (!ulist)
ulist = Ptr->GetUsers();
for (CUList::iterator i = ulist->begin(); i != ulist->end(); i++)
i->second = i->first->GetFullHost();
}
return 0;
}
};
MODULE_INIT(ModuleUHNames)
\ No newline at end of file +/* +------------------------------------+ + * | 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 "users.h" +#include "channels.h" +#include "modules.h" + +static const char* dummy = "ON"; + +/* $ModDesc: Provides aliases of commands. */ + +class ModuleUHNames : public Module +{ + CUList nl; + public: + + ModuleUHNames(InspIRCd* Me) + : Module(Me) + { + } + + void Implements(char* List) + { + List[I_OnSyncUserMetaData] = List[I_OnPreCommand] = List[I_OnUserList] = List[I_On005Numeric] = 1; + } + + virtual ~ModuleUHNames() + { + } + + void OnSyncUserMetaData(userrec* user, Module* proto,void* opaque, const std::string &extname, bool displayable) + { + if ((displayable) && (extname == "UHNAMES")) + proto->ProtoSendMetaData(opaque, TYPE_USER, user, extname, "Enabled"); + } + + virtual Version GetVersion() + { + return Version(1,1,0,1,VF_VENDOR,API_VERSION); + } + + virtual void On005Numeric(std::string &output) + { + output.append(" UHNAMES"); + } + + Priority Prioritize() + { + return (Priority)ServerInstance->PriorityBefore("m_namesx.so"); + } + + virtual int OnPreCommand(const std::string &command, const char** parameters, int pcnt, userrec *user, bool validated, const std::string &original_line) + { + irc::string c = command.c_str(); + /* We don't actually create a proper command handler class for PROTOCTL, + * because other modules might want to have PROTOCTL hooks too. + * Therefore, we just hook its as an unvalidated command therefore we + * can capture it even if it doesnt exist! :-) + */ + if (c == "PROTOCTL") + { + if ((pcnt) && (!strcasecmp(parameters[0],"UHNAMES"))) + { + user->Extend("UHNAMES",dummy); + return 1; + } + } + return 0; + } + + /* IMPORTANT: This must be prioritized above NAMESX! */ + virtual int OnUserList(userrec* user, chanrec* Ptr, CUList* &ulist) + { + if (user->GetExt("UHNAMES")) + { + if (!ulist) + ulist = Ptr->GetUsers(); + + for (CUList::iterator i = ulist->begin(); i != ulist->end(); i++) + i->second = i->first->GetFullHost(); + } + return 0; + } +}; + +MODULE_INIT(ModuleUHNames) + diff --git a/src/modules/m_uninvite.cpp b/src/modules/m_uninvite.cpp index bcf18b308..ab748663c 100644 --- a/src/modules/m_uninvite.cpp +++ b/src/modules/m_uninvite.cpp @@ -1 +1,107 @@ -/* +------------------------------------+
* | 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.
*
* ---------------------------------------------------
*/
/* $ModDesc: Provides the UNINVITE command which lets users un-invite other users from channels (!) */
#include "inspircd.h"
#include "users.h"
#include "channels.h"
#include "modules.h"
#include "configreader.h"
/** Handle /UNINVITE
*/
class cmd_uninvite : public command_t
{
public:
cmd_uninvite (InspIRCd* Instance) : command_t(Instance,"UNINVITE", 0, 2)
{
this->source = "m_uninvite.so";
syntax = "<nick> <channel>";
}
CmdResult Handle (const char** parameters, int pcnt, userrec *user)
{
userrec* u = ServerInstance->FindNick(parameters[0]);
chanrec* c = ServerInstance->FindChan(parameters[1]);
if ((!c) || (!u))
{
if (!c)
{
user->WriteServ("401 %s %s :No such nick/channel",user->nick, parameters[1]);
}
else
{
user->WriteServ("401 %s %s :No such nick/channel",user->nick, parameters[0]);
}
return CMD_FAILURE;
}
if (c->modes[CM_INVITEONLY])
{
if (c->GetStatus(user) < STATUS_HOP)
{
user->WriteServ("482 %s %s :You must be at least a half-operator to change modes on this channel",user->nick, c->name);
return CMD_FAILURE;
}
}
irc::string xname(c->name);
if (!u->IsInvited(xname))
{
user->WriteServ("491 %s %s %s :Is not invited to channel %s",user->nick,u->nick,c->name,c->name);
return CMD_FAILURE;
}
if (!c->HasUser(user))
{
user->WriteServ("492 %s %s :You're not on that channel!",user->nick, c->name);
return CMD_FAILURE;
}
u->RemoveInvite(xname);
user->WriteServ("494 %s %s %s :Uninvited",user->nick,c->name,u->nick);
u->WriteServ("493 %s :You were uninvited from %s by %s",u->nick,c->name,user->nick);
c->WriteChannelWithServ(ServerInstance->Config->ServerName, "NOTICE %s :*** %s uninvited %s.", c->name, user->nick, u->nick);
return CMD_SUCCESS;
}
};
class ModuleUninvite : public Module
{
cmd_uninvite *mycommand;
public:
ModuleUninvite(InspIRCd* Me) : Module(Me)
{
mycommand = new cmd_uninvite(ServerInstance);
ServerInstance->AddCommand(mycommand);
}
virtual ~ModuleUninvite()
{
}
virtual Version GetVersion()
{
return Version(1, 1, 0, 0, VF_VENDOR, API_VERSION);
}
};
MODULE_INIT(ModuleUninvite)
\ No newline at end of file +/* +------------------------------------+ + * | 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. + * + * --------------------------------------------------- + */ + +/* $ModDesc: Provides the UNINVITE command which lets users un-invite other users from channels (!) */ + +#include "inspircd.h" +#include "users.h" +#include "channels.h" +#include "modules.h" +#include "configreader.h" + +/** Handle /UNINVITE + */ +class cmd_uninvite : public command_t +{ + public: + cmd_uninvite (InspIRCd* Instance) : command_t(Instance,"UNINVITE", 0, 2) + { + this->source = "m_uninvite.so"; + syntax = "<nick> <channel>"; + } + + CmdResult Handle (const char** parameters, int pcnt, userrec *user) + { + userrec* u = ServerInstance->FindNick(parameters[0]); + chanrec* c = ServerInstance->FindChan(parameters[1]); + + if ((!c) || (!u)) + { + if (!c) + { + user->WriteServ("401 %s %s :No such nick/channel",user->nick, parameters[1]); + } + else + { + user->WriteServ("401 %s %s :No such nick/channel",user->nick, parameters[0]); + } + + return CMD_FAILURE; + } + + if (c->modes[CM_INVITEONLY]) + { + if (c->GetStatus(user) < STATUS_HOP) + { + user->WriteServ("482 %s %s :You must be at least a half-operator to change modes on this channel",user->nick, c->name); + return CMD_FAILURE; + } + } + + irc::string xname(c->name); + + if (!u->IsInvited(xname)) + { + user->WriteServ("491 %s %s %s :Is not invited to channel %s",user->nick,u->nick,c->name,c->name); + return CMD_FAILURE; + } + if (!c->HasUser(user)) + { + user->WriteServ("492 %s %s :You're not on that channel!",user->nick, c->name); + return CMD_FAILURE; + } + + u->RemoveInvite(xname); + user->WriteServ("494 %s %s %s :Uninvited",user->nick,c->name,u->nick); + u->WriteServ("493 %s :You were uninvited from %s by %s",u->nick,c->name,user->nick); + c->WriteChannelWithServ(ServerInstance->Config->ServerName, "NOTICE %s :*** %s uninvited %s.", c->name, user->nick, u->nick); + + return CMD_SUCCESS; + } +}; + +class ModuleUninvite : public Module +{ + cmd_uninvite *mycommand; + + public: + + ModuleUninvite(InspIRCd* Me) : Module(Me) + { + + mycommand = new cmd_uninvite(ServerInstance); + ServerInstance->AddCommand(mycommand); + } + + virtual ~ModuleUninvite() + { + } + + virtual Version GetVersion() + { + return Version(1, 1, 0, 0, VF_VENDOR, API_VERSION); + } +}; + +MODULE_INIT(ModuleUninvite) + diff --git a/src/modules/m_userip.cpp b/src/modules/m_userip.cpp index 979ee6112..296d52300 100644 --- a/src/modules/m_userip.cpp +++ b/src/modules/m_userip.cpp @@ -1 +1,86 @@ -/* +------------------------------------+
* | 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 "users.h"
#include "channels.h"
#include "modules.h"
/* $ModDesc: Provides support for USERIP command */
/** Handle /USERIP
*/
class cmd_userip : public command_t
{
public:
cmd_userip (InspIRCd* Instance) : command_t(Instance,"USERIP", 'o', 1)
{
this->source = "m_userip.so";
syntax = "<nick>{,<nick>}";
}
CmdResult Handle (const char** parameters, int pcnt, userrec *user)
{
std::string retbuf = std::string("340 ") + user->nick + " :";
for (int i = 0; i < pcnt; i++)
{
userrec *u = ServerInstance->FindNick(parameters[i]);
if ((u) && (u->registered == REG_ALL))
{
retbuf = retbuf + u->nick + (IS_OPER(u) ? "*" : "") + "=+" + u->ident + "@" + u->GetIPString() + " ";
}
}
user->WriteServ(retbuf);
/* Dont send to the network */
return CMD_FAILURE;
}
};
class ModuleUserIP : public Module
{
cmd_userip* mycommand;
public:
ModuleUserIP(InspIRCd* Me)
: Module(Me)
{
mycommand = new cmd_userip(ServerInstance);
ServerInstance->AddCommand(mycommand);
}
void Implements(char* List)
{
List[I_On005Numeric] = 1;
}
virtual void On005Numeric(std::string &output)
{
output = output + std::string(" USERIP");
}
virtual ~ModuleUserIP()
{
}
virtual Version GetVersion()
{
return Version(1,1,0,1,VF_VENDOR,API_VERSION);
}
};
MODULE_INIT(ModuleUserIP)
\ No newline at end of file +/* +------------------------------------+ + * | 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 "users.h" +#include "channels.h" +#include "modules.h" + +/* $ModDesc: Provides support for USERIP command */ + +/** Handle /USERIP + */ +class cmd_userip : public command_t +{ + public: + cmd_userip (InspIRCd* Instance) : command_t(Instance,"USERIP", 'o', 1) + { + this->source = "m_userip.so"; + syntax = "<nick>{,<nick>}"; + } + + CmdResult Handle (const char** parameters, int pcnt, userrec *user) + { + std::string retbuf = std::string("340 ") + user->nick + " :"; + + for (int i = 0; i < pcnt; i++) + { + userrec *u = ServerInstance->FindNick(parameters[i]); + if ((u) && (u->registered == REG_ALL)) + { + retbuf = retbuf + u->nick + (IS_OPER(u) ? "*" : "") + "=+" + u->ident + "@" + u->GetIPString() + " "; + } + } + + user->WriteServ(retbuf); + + /* Dont send to the network */ + return CMD_FAILURE; + } +}; + +class ModuleUserIP : public Module +{ + cmd_userip* mycommand; + public: + ModuleUserIP(InspIRCd* Me) + : Module(Me) + { + + mycommand = new cmd_userip(ServerInstance); + ServerInstance->AddCommand(mycommand); + } + + void Implements(char* List) + { + List[I_On005Numeric] = 1; + } + + virtual void On005Numeric(std::string &output) + { + output = output + std::string(" USERIP"); + } + + virtual ~ModuleUserIP() + { + } + + virtual Version GetVersion() + { + return Version(1,1,0,1,VF_VENDOR,API_VERSION); + } + +}; + +MODULE_INIT(ModuleUserIP) + diff --git a/src/modules/m_vhost.cpp b/src/modules/m_vhost.cpp index 10fc76f05..c77a3f85f 100644 --- a/src/modules/m_vhost.cpp +++ b/src/modules/m_vhost.cpp @@ -1 +1,95 @@ -/* +------------------------------------+
* | 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 "users.h"
#include "channels.h"
#include "modules.h"
/* $ModDesc: Provides masking of user hostnames via traditional /VHOST command */
static ConfigReader* Conf;
/** Handle /VHOST
*/
class cmd_vhost : public command_t
{
public:
cmd_vhost (InspIRCd* Instance) : command_t(Instance,"VHOST", 0, 2)
{
this->source = "m_vhost.so";
syntax = "<username> <password>";
}
CmdResult Handle (const char** parameters, int pcnt, userrec *user)
{
for (int index = 0; index < Conf->Enumerate("vhost"); index++)
{
std::string mask = Conf->ReadValue("vhost","host",index);
std::string username = Conf->ReadValue("vhost","user",index);
std::string pass = Conf->ReadValue("vhost","pass",index);
if ((!strcmp(parameters[0],username.c_str())) && (!strcmp(parameters[1],pass.c_str())))
{
if (!mask.empty())
{
user->WriteServ("NOTICE "+std::string(user->nick)+" :Setting your VHost: " + mask);
user->ChangeDisplayedHost(mask.c_str());
return CMD_FAILURE;
}
}
}
user->WriteServ("NOTICE "+std::string(user->nick)+" :Invalid username or password.");
return CMD_FAILURE;
}
};
class ModuleVHost : public Module
{
private:
cmd_vhost* mycommand;
public:
ModuleVHost(InspIRCd* Me) : Module(Me)
{
Conf = new ConfigReader(ServerInstance);
mycommand = new cmd_vhost(ServerInstance);
ServerInstance->AddCommand(mycommand);
}
virtual ~ModuleVHost()
{
DELETE(Conf);
}
void Implements(char* List)
{
List[I_OnRehash] = 1;
}
virtual void OnRehash(userrec* user, const std::string ¶meter)
{
DELETE(Conf);
Conf = new ConfigReader(ServerInstance);
}
virtual Version GetVersion()
{
return Version(1,1,0,1,VF_VENDOR,API_VERSION);
}
};
MODULE_INIT(ModuleVHost)
\ No newline at end of file +/* +------------------------------------+ + * | 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 "users.h" +#include "channels.h" +#include "modules.h" + +/* $ModDesc: Provides masking of user hostnames via traditional /VHOST command */ + +static ConfigReader* Conf; + +/** Handle /VHOST + */ +class cmd_vhost : public command_t +{ + public: + cmd_vhost (InspIRCd* Instance) : command_t(Instance,"VHOST", 0, 2) + { + this->source = "m_vhost.so"; + syntax = "<username> <password>"; + } + + CmdResult Handle (const char** parameters, int pcnt, userrec *user) + { + for (int index = 0; index < Conf->Enumerate("vhost"); index++) + { + std::string mask = Conf->ReadValue("vhost","host",index); + std::string username = Conf->ReadValue("vhost","user",index); + std::string pass = Conf->ReadValue("vhost","pass",index); + if ((!strcmp(parameters[0],username.c_str())) && (!strcmp(parameters[1],pass.c_str()))) + { + if (!mask.empty()) + { + user->WriteServ("NOTICE "+std::string(user->nick)+" :Setting your VHost: " + mask); + user->ChangeDisplayedHost(mask.c_str()); + return CMD_FAILURE; + } + } + } + user->WriteServ("NOTICE "+std::string(user->nick)+" :Invalid username or password."); + return CMD_FAILURE; + } +}; + +class ModuleVHost : public Module +{ + private: + + cmd_vhost* mycommand; + + public: + ModuleVHost(InspIRCd* Me) : Module(Me) + { + + Conf = new ConfigReader(ServerInstance); + mycommand = new cmd_vhost(ServerInstance); + ServerInstance->AddCommand(mycommand); + } + + virtual ~ModuleVHost() + { + DELETE(Conf); + } + + void Implements(char* List) + { + List[I_OnRehash] = 1; + } + + virtual void OnRehash(userrec* user, const std::string ¶meter) + { + DELETE(Conf); + Conf = new ConfigReader(ServerInstance); + } + + virtual Version GetVersion() + { + return Version(1,1,0,1,VF_VENDOR,API_VERSION); + } + +}; + +MODULE_INIT(ModuleVHost) + diff --git a/src/modules/m_watch.cpp b/src/modules/m_watch.cpp index 2f847661e..552e3317a 100644 --- a/src/modules/m_watch.cpp +++ b/src/modules/m_watch.cpp @@ -1 +1,472 @@ -/* +------------------------------------+
* | 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 "users.h"
#include "channels.h"
#include "modules.h"
#include "hashcomp.h"
/* $ModDesc: Provides support for the /WATCH command */
/* This module has been refactored to provide a very efficient (in terms of cpu time)
* implementation of /WATCH.
*
* To improve the efficiency of watch, many lists are kept. The first primary list is
* a hash_map of who's being watched by who. For example:
*
* KEY: Brain ---> Watched by: Boo, w00t, Om
* KEY: Boo ---> Watched by: Brain, w00t
*
* This is used when we want to tell all the users that are watching someone that
* they are now available or no longer available. For example, if the hash was
* populated as shown above, then when Brain signs on, messages are sent to Boo, w00t
* and Om by reading their 'watched by' list. When this occurs, their online status
* in each of these users lists (see below) is also updated.
*
* Each user also has a seperate (smaller) map attached to their userrec whilst they
* have any watch entries, which is managed by class Extensible. When they add or remove
* a watch entry from their list, it is inserted here, as well as the main list being
* maintained. This map also contains the user's online status. For users that are
* offline, the key points at an empty string, and for users that are online, the key
* points at a string containing "users-ident users-host users-signon-time". This is
* stored in this manner so that we don't have to FindUser() to fetch this info, the
* users signon can populate the field for us.
*
* For example, going again on the example above, this would be w00t's watchlist:
*
* KEY: Boo ---> Status: "Boo brains.sexy.babe 535342348"
* KEY: Brain ---> Status: ""
*
* In this list we can see that Boo is online, and Brain is offline. We can then
* use this list for 'WATCH L', and 'WATCH S' can be implemented as a combination
* of the above two data structures, with minimum CPU penalty for doing so.
*
* In short, the least efficient this ever gets is O(n), and thats only because
* there are parts that *must* loop (e.g. telling all users that are watching a
* nick that the user online), however this is a *major* improvement over the
* 1.0 implementation, which in places had O(n^n) and worse in it, because this
* implementation scales based upon the sizes of the watch entries, whereas the
* old system would scale (or not as the case may be) according to the total number
* of users using WATCH.
*/
/*
* Before you start screaming, this definition is only used here, so moving it to a header is pointless.
* Yes, it's horrid. Blame cl for being different. -- w00t
*/
#ifdef WINDOWS
typedef nspace::hash_map<irc::string, std::deque<userrec*>, nspace::hash_compare<irc::string, less<irc::string> > > watchentries;
#else
typedef nspace::hash_map<irc::string, std::deque<userrec*>, nspace::hash<irc::string> > watchentries;
#endif
typedef std::map<irc::string, std::string> watchlist;
/* Who's watching each nickname.
* NOTE: We do NOT iterate this to display a user's WATCH list!
* See the comments above!
*/
watchentries* whos_watching_me;
/** Handle /WATCH
*/
class cmd_watch : public command_t
{
unsigned int& MAX_WATCH;
public:
CmdResult remove_watch(userrec* user, const char* nick)
{
// removing an item from the list
if (!ServerInstance->IsNick(nick))
{
user->WriteServ("942 %s %s :Invalid nickname", user->nick, nick);
return CMD_FAILURE;
}
watchlist* wl;
if (user->GetExt("watchlist", wl))
{
/* Yup, is on my list */
watchlist::iterator n = wl->find(nick);
if (n != wl->end())
{
if (!n->second.empty())
user->WriteServ("602 %s %s %s :stopped watching", user->nick, n->first.c_str(), n->second.c_str());
else
user->WriteServ("602 %s %s * * 0 :stopped watching", user->nick, nick);
wl->erase(n);
}
if (!wl->size())
{
user->Shrink("watchlist");
delete wl;
}
watchentries::iterator x = whos_watching_me->find(nick);
if (x != whos_watching_me->end())
{
/* People are watching this user, am i one of them? */
std::deque<userrec*>::iterator n = std::find(x->second.begin(), x->second.end(), user);
if (n != x->second.end())
/* I'm no longer watching you... */
x->second.erase(n);
if (!x->second.size())
whos_watching_me->erase(nick);
}
}
/* This might seem confusing, but we return CMD_FAILURE
* to indicate that this message shouldnt be routed across
* the network to other linked servers.
*/
return CMD_FAILURE;
}
CmdResult add_watch(userrec* user, const char* nick)
{
if (!ServerInstance->IsNick(nick))
{
user->WriteServ("942 %s %s :Invalid nickname",user->nick,nick);
return CMD_FAILURE;
}
watchlist* wl;
if (!user->GetExt("watchlist", wl))
{
wl = new watchlist();
user->Extend("watchlist", wl);
}
if (wl->size() == MAX_WATCH)
{
user->WriteServ("512 %s %s :Too many WATCH entries", user->nick, nick);
return CMD_FAILURE;
}
watchlist::iterator n = wl->find(nick);
if (n == wl->end())
{
/* Don't already have the user on my watch list, proceed */
watchentries::iterator x = whos_watching_me->find(nick);
if (x != whos_watching_me->end())
{
/* People are watching this user, add myself */
x->second.push_back(user);
}
else
{
std::deque<userrec*> newlist;
newlist.push_back(user);
(*(whos_watching_me))[nick] = newlist;
}
userrec* target = ServerInstance->FindNick(nick);
if (target)
{
if (target->Visibility && !target->Visibility->VisibleTo(user))
{
(*wl)[nick] = "";
user->WriteServ("605 %s %s * * 0 :is offline",user->nick, nick);
return CMD_FAILURE;
}
(*wl)[nick] = std::string(target->ident).append(" ").append(target->dhost).append(" ").append(ConvToStr(target->age));
user->WriteServ("604 %s %s %s :is online",user->nick, nick, (*wl)[nick].c_str());
}
else
{
(*wl)[nick] = "";
user->WriteServ("605 %s %s * * 0 :is offline",user->nick, nick);
}
}
return CMD_FAILURE;
}
cmd_watch (InspIRCd* Instance, unsigned int &maxwatch) : command_t(Instance,"WATCH",0,0), MAX_WATCH(maxwatch)
{
this->source = "m_watch.so";
syntax = "[C|L|S]|[+|-<nick>]";
}
CmdResult Handle (const char** parameters, int pcnt, userrec *user)
{
if (!pcnt)
{
watchlist* wl;
if (user->GetExt("watchlist", wl))
{
for (watchlist::iterator q = wl->begin(); q != wl->end(); q++)
{
if (!q->second.empty())
user->WriteServ("604 %s %s %s :is online", user->nick, q->first.c_str(), q->second.c_str());
}
}
user->WriteServ("607 %s :End of WATCH list",user->nick);
}
else if (pcnt > 0)
{
for (int x = 0; x < pcnt; x++)
{
const char *nick = parameters[x];
if (!strcasecmp(nick,"C"))
{
// watch clear
watchlist* wl;
if (user->GetExt("watchlist", wl))
{
for (watchlist::iterator i = wl->begin(); i != wl->end(); i++)
{
watchentries::iterator x = whos_watching_me->find(i->first);
if (x != whos_watching_me->end())
{
/* People are watching this user, am i one of them? */
std::deque<userrec*>::iterator n = std::find(x->second.begin(), x->second.end(), user);
if (n != x->second.end())
/* I'm no longer watching you... */
x->second.erase(n);
if (!x->second.size())
whos_watching_me->erase(user->nick);
}
}
delete wl;
user->Shrink("watchlist");
}
}
else if (!strcasecmp(nick,"L"))
{
watchlist* wl;
if (user->GetExt("watchlist", wl))
{
for (watchlist::iterator q = wl->begin(); q != wl->end(); q++)
{
if (!q->second.empty())
user->WriteServ("604 %s %s %s :is online", user->nick, q->first.c_str(), q->second.c_str());
else
user->WriteServ("605 %s %s * * 0 :is offline", user->nick, q->first.c_str());
}
}
user->WriteServ("607 %s :End of WATCH list",user->nick);
}
else if (!strcasecmp(nick,"S"))
{
watchlist* wl;
int you_have = 0;
int youre_on = 0;
std::string list;
if (user->GetExt("watchlist", wl))
{
for (watchlist::iterator q = wl->begin(); q != wl->end(); q++)
list.append(q->first.c_str()).append(" ");
you_have = wl->size();
}
watchentries::iterator x = whos_watching_me->find(user->nick);
if (x != whos_watching_me->end())
youre_on = x->second.size();
user->WriteServ("603 %s :You have %d and are on %d WATCH entries", user->nick, you_have, youre_on);
user->WriteServ("606 %s :%s",user->nick, list.c_str());
user->WriteServ("607 %s :End of WATCH S",user->nick);
}
else if (nick[0] == '-')
{
nick++;
remove_watch(user, nick);
}
else if (nick[0] == '+')
{
nick++;
add_watch(user, nick);
}
}
}
/* So that spanningtree doesnt pass the WATCH commands to the network! */
return CMD_FAILURE;
}
};
class Modulewatch : public Module
{
cmd_watch* mycommand;
unsigned int maxwatch;
public:
Modulewatch(InspIRCd* Me)
: Module(Me), maxwatch(32)
{
OnRehash(NULL, "");
whos_watching_me = new watchentries();
mycommand = new cmd_watch(ServerInstance, maxwatch);
ServerInstance->AddCommand(mycommand);
}
virtual void OnRehash(userrec* user, const std::string ¶meter)
{
ConfigReader Conf(ServerInstance);
maxwatch = Conf.ReadInteger("watch", "maxentries", 0, true);
if (!maxwatch)
maxwatch = 32;
}
void Implements(char* List)
{
List[I_OnRehash] = List[I_OnGarbageCollect] = List[I_OnCleanup] = List[I_OnUserQuit] = List[I_OnPostConnect] = List[I_OnUserPostNick] = List[I_On005Numeric] = 1;
}
virtual void OnUserQuit(userrec* user, const std::string &reason, const std::string &oper_message)
{
watchentries::iterator x = whos_watching_me->find(user->nick);
if (x != whos_watching_me->end())
{
for (std::deque<userrec*>::iterator n = x->second.begin(); n != x->second.end(); n++)
{
if (!user->Visibility || user->Visibility->VisibleTo(user))
(*n)->WriteServ("601 %s %s %s %s %lu :went offline", (*n)->nick ,user->nick, user->ident, user->dhost, ServerInstance->Time());
watchlist* wl;
if ((*n)->GetExt("watchlist", wl))
/* We were on somebody's notify list, set ourselves offline */
(*wl)[user->nick] = "";
}
}
/* Now im quitting, if i have a notify list, im no longer watching anyone */
watchlist* wl;
if (user->GetExt("watchlist", wl))
{
/* Iterate every user on my watch list, and take me out of the whos_watching_me map for each one we're watching */
for (watchlist::iterator i = wl->begin(); i != wl->end(); i++)
{
watchentries::iterator x = whos_watching_me->find(i->first);
if (x != whos_watching_me->end())
{
/* People are watching this user, am i one of them? */
std::deque<userrec*>::iterator n = std::find(x->second.begin(), x->second.end(), user);
if (n != x->second.end())
/* I'm no longer watching you... */
x->second.erase(n);
if (!x->second.size())
whos_watching_me->erase(user->nick);
}
}
/* User's quitting, we're done with this. */
delete wl;
}
}
virtual void OnGarbageCollect()
{
watchentries* old_watch = whos_watching_me;
whos_watching_me = new watchentries();
for (watchentries::const_iterator n = old_watch->begin(); n != old_watch->end(); n++)
whos_watching_me->insert(*n);
delete old_watch;
}
virtual void OnCleanup(int target_type, void* item)
{
if (target_type == TYPE_USER)
{
watchlist* wl;
userrec* user = (userrec*)item;
if (user->GetExt("watchlist", wl))
{
user->Shrink("watchlist");
delete wl;
}
}
}
virtual void OnPostConnect(userrec* user)
{
watchentries::iterator x = whos_watching_me->find(user->nick);
if (x != whos_watching_me->end())
{
for (std::deque<userrec*>::iterator n = x->second.begin(); n != x->second.end(); n++)
{
if (!user->Visibility || user->Visibility->VisibleTo(user))
(*n)->WriteServ("600 %s %s %s %s %lu :arrived online", (*n)->nick, user->nick, user->ident, user->dhost, user->age);
watchlist* wl;
if ((*n)->GetExt("watchlist", wl))
/* We were on somebody's notify list, set ourselves online */
(*wl)[user->nick] = std::string(user->ident).append(" ").append(user->dhost).append(" ").append(ConvToStr(user->age));
}
}
}
virtual void OnUserPostNick(userrec* user, const std::string &oldnick)
{
watchentries::iterator new_online = whos_watching_me->find(user->nick);
watchentries::iterator new_offline = whos_watching_me->find(assign(oldnick));
if (new_online != whos_watching_me->end())
{
for (std::deque<userrec*>::iterator n = new_online->second.begin(); n != new_online->second.end(); n++)
{
watchlist* wl;
if ((*n)->GetExt("watchlist", wl))
{
(*wl)[user->nick] = std::string(user->ident).append(" ").append(user->dhost).append(" ").append(ConvToStr(user->age));
if (!user->Visibility || user->Visibility->VisibleTo(user))
(*n)->WriteServ("600 %s %s %s :arrived online", (*n)->nick, user->nick, (*wl)[user->nick].c_str());
}
}
}
if (new_offline != whos_watching_me->end())
{
for (std::deque<userrec*>::iterator n = new_offline->second.begin(); n != new_offline->second.end(); n++)
{
watchlist* wl;
if ((*n)->GetExt("watchlist", wl))
{
if (!user->Visibility || user->Visibility->VisibleTo(user))
(*n)->WriteServ("601 %s %s %s %s %lu :went offline", (*n)->nick, oldnick.c_str(), user->ident, user->dhost, user->age);
(*wl)[oldnick.c_str()] = "";
}
}
}
}
virtual void On005Numeric(std::string &output)
{
// we don't really have a limit...
output = output + " WATCH=" + ConvToStr(maxwatch);
}
virtual ~Modulewatch()
{
delete whos_watching_me;
}
virtual Version GetVersion()
{
return Version(1,1,0,1,VF_VENDOR,API_VERSION);
}
};
MODULE_INIT(Modulewatch)
\ No newline at end of file +/* +------------------------------------+ + * | 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 "users.h" +#include "channels.h" +#include "modules.h" +#include "hashcomp.h" + +/* $ModDesc: Provides support for the /WATCH command */ + +/* This module has been refactored to provide a very efficient (in terms of cpu time) + * implementation of /WATCH. + * + * To improve the efficiency of watch, many lists are kept. The first primary list is + * a hash_map of who's being watched by who. For example: + * + * KEY: Brain ---> Watched by: Boo, w00t, Om + * KEY: Boo ---> Watched by: Brain, w00t + * + * This is used when we want to tell all the users that are watching someone that + * they are now available or no longer available. For example, if the hash was + * populated as shown above, then when Brain signs on, messages are sent to Boo, w00t + * and Om by reading their 'watched by' list. When this occurs, their online status + * in each of these users lists (see below) is also updated. + * + * Each user also has a seperate (smaller) map attached to their userrec whilst they + * have any watch entries, which is managed by class Extensible. When they add or remove + * a watch entry from their list, it is inserted here, as well as the main list being + * maintained. This map also contains the user's online status. For users that are + * offline, the key points at an empty string, and for users that are online, the key + * points at a string containing "users-ident users-host users-signon-time". This is + * stored in this manner so that we don't have to FindUser() to fetch this info, the + * users signon can populate the field for us. + * + * For example, going again on the example above, this would be w00t's watchlist: + * + * KEY: Boo ---> Status: "Boo brains.sexy.babe 535342348" + * KEY: Brain ---> Status: "" + * + * In this list we can see that Boo is online, and Brain is offline. We can then + * use this list for 'WATCH L', and 'WATCH S' can be implemented as a combination + * of the above two data structures, with minimum CPU penalty for doing so. + * + * In short, the least efficient this ever gets is O(n), and thats only because + * there are parts that *must* loop (e.g. telling all users that are watching a + * nick that the user online), however this is a *major* improvement over the + * 1.0 implementation, which in places had O(n^n) and worse in it, because this + * implementation scales based upon the sizes of the watch entries, whereas the + * old system would scale (or not as the case may be) according to the total number + * of users using WATCH. + */ + +/* + * Before you start screaming, this definition is only used here, so moving it to a header is pointless. + * Yes, it's horrid. Blame cl for being different. -- w00t + */ +#ifdef WINDOWS +typedef nspace::hash_map<irc::string, std::deque<userrec*>, nspace::hash_compare<irc::string, less<irc::string> > > watchentries; +#else +typedef nspace::hash_map<irc::string, std::deque<userrec*>, nspace::hash<irc::string> > watchentries; +#endif +typedef std::map<irc::string, std::string> watchlist; + +/* Who's watching each nickname. + * NOTE: We do NOT iterate this to display a user's WATCH list! + * See the comments above! + */ +watchentries* whos_watching_me; + +/** Handle /WATCH + */ +class cmd_watch : public command_t +{ + unsigned int& MAX_WATCH; + public: + CmdResult remove_watch(userrec* user, const char* nick) + { + // removing an item from the list + if (!ServerInstance->IsNick(nick)) + { + user->WriteServ("942 %s %s :Invalid nickname", user->nick, nick); + return CMD_FAILURE; + } + + watchlist* wl; + if (user->GetExt("watchlist", wl)) + { + /* Yup, is on my list */ + watchlist::iterator n = wl->find(nick); + if (n != wl->end()) + { + if (!n->second.empty()) + user->WriteServ("602 %s %s %s :stopped watching", user->nick, n->first.c_str(), n->second.c_str()); + else + user->WriteServ("602 %s %s * * 0 :stopped watching", user->nick, nick); + + wl->erase(n); + } + + if (!wl->size()) + { + user->Shrink("watchlist"); + delete wl; + } + + watchentries::iterator x = whos_watching_me->find(nick); + if (x != whos_watching_me->end()) + { + /* People are watching this user, am i one of them? */ + std::deque<userrec*>::iterator n = std::find(x->second.begin(), x->second.end(), user); + if (n != x->second.end()) + /* I'm no longer watching you... */ + x->second.erase(n); + + if (!x->second.size()) + whos_watching_me->erase(nick); + } + } + + /* This might seem confusing, but we return CMD_FAILURE + * to indicate that this message shouldnt be routed across + * the network to other linked servers. + */ + return CMD_FAILURE; + } + + CmdResult add_watch(userrec* user, const char* nick) + { + if (!ServerInstance->IsNick(nick)) + { + user->WriteServ("942 %s %s :Invalid nickname",user->nick,nick); + return CMD_FAILURE; + } + + watchlist* wl; + if (!user->GetExt("watchlist", wl)) + { + wl = new watchlist(); + user->Extend("watchlist", wl); + } + + if (wl->size() == MAX_WATCH) + { + user->WriteServ("512 %s %s :Too many WATCH entries", user->nick, nick); + return CMD_FAILURE; + } + + watchlist::iterator n = wl->find(nick); + if (n == wl->end()) + { + /* Don't already have the user on my watch list, proceed */ + watchentries::iterator x = whos_watching_me->find(nick); + if (x != whos_watching_me->end()) + { + /* People are watching this user, add myself */ + x->second.push_back(user); + } + else + { + std::deque<userrec*> newlist; + newlist.push_back(user); + (*(whos_watching_me))[nick] = newlist; + } + + userrec* target = ServerInstance->FindNick(nick); + if (target) + { + if (target->Visibility && !target->Visibility->VisibleTo(user)) + { + (*wl)[nick] = ""; + user->WriteServ("605 %s %s * * 0 :is offline",user->nick, nick); + return CMD_FAILURE; + } + + (*wl)[nick] = std::string(target->ident).append(" ").append(target->dhost).append(" ").append(ConvToStr(target->age)); + user->WriteServ("604 %s %s %s :is online",user->nick, nick, (*wl)[nick].c_str()); + } + else + { + (*wl)[nick] = ""; + user->WriteServ("605 %s %s * * 0 :is offline",user->nick, nick); + } + } + + return CMD_FAILURE; + } + + cmd_watch (InspIRCd* Instance, unsigned int &maxwatch) : command_t(Instance,"WATCH",0,0), MAX_WATCH(maxwatch) + { + this->source = "m_watch.so"; + syntax = "[C|L|S]|[+|-<nick>]"; + } + + CmdResult Handle (const char** parameters, int pcnt, userrec *user) + { + if (!pcnt) + { + watchlist* wl; + if (user->GetExt("watchlist", wl)) + { + for (watchlist::iterator q = wl->begin(); q != wl->end(); q++) + { + if (!q->second.empty()) + user->WriteServ("604 %s %s %s :is online", user->nick, q->first.c_str(), q->second.c_str()); + } + } + user->WriteServ("607 %s :End of WATCH list",user->nick); + } + else if (pcnt > 0) + { + for (int x = 0; x < pcnt; x++) + { + const char *nick = parameters[x]; + if (!strcasecmp(nick,"C")) + { + // watch clear + watchlist* wl; + if (user->GetExt("watchlist", wl)) + { + for (watchlist::iterator i = wl->begin(); i != wl->end(); i++) + { + watchentries::iterator x = whos_watching_me->find(i->first); + if (x != whos_watching_me->end()) + { + /* People are watching this user, am i one of them? */ + std::deque<userrec*>::iterator n = std::find(x->second.begin(), x->second.end(), user); + if (n != x->second.end()) + /* I'm no longer watching you... */ + x->second.erase(n); + + if (!x->second.size()) + whos_watching_me->erase(user->nick); + } + } + + delete wl; + user->Shrink("watchlist"); + } + } + else if (!strcasecmp(nick,"L")) + { + watchlist* wl; + if (user->GetExt("watchlist", wl)) + { + for (watchlist::iterator q = wl->begin(); q != wl->end(); q++) + { + if (!q->second.empty()) + user->WriteServ("604 %s %s %s :is online", user->nick, q->first.c_str(), q->second.c_str()); + else + user->WriteServ("605 %s %s * * 0 :is offline", user->nick, q->first.c_str()); + } + } + user->WriteServ("607 %s :End of WATCH list",user->nick); + } + else if (!strcasecmp(nick,"S")) + { + watchlist* wl; + int you_have = 0; + int youre_on = 0; + std::string list; + + if (user->GetExt("watchlist", wl)) + { + for (watchlist::iterator q = wl->begin(); q != wl->end(); q++) + list.append(q->first.c_str()).append(" "); + you_have = wl->size(); + } + + watchentries::iterator x = whos_watching_me->find(user->nick); + if (x != whos_watching_me->end()) + youre_on = x->second.size(); + + user->WriteServ("603 %s :You have %d and are on %d WATCH entries", user->nick, you_have, youre_on); + user->WriteServ("606 %s :%s",user->nick, list.c_str()); + user->WriteServ("607 %s :End of WATCH S",user->nick); + } + else if (nick[0] == '-') + { + nick++; + remove_watch(user, nick); + } + else if (nick[0] == '+') + { + nick++; + add_watch(user, nick); + } + } + } + /* So that spanningtree doesnt pass the WATCH commands to the network! */ + return CMD_FAILURE; + } +}; + +class Modulewatch : public Module +{ + cmd_watch* mycommand; + unsigned int maxwatch; + public: + + Modulewatch(InspIRCd* Me) + : Module(Me), maxwatch(32) + { + OnRehash(NULL, ""); + whos_watching_me = new watchentries(); + mycommand = new cmd_watch(ServerInstance, maxwatch); + ServerInstance->AddCommand(mycommand); + } + + virtual void OnRehash(userrec* user, const std::string ¶meter) + { + ConfigReader Conf(ServerInstance); + maxwatch = Conf.ReadInteger("watch", "maxentries", 0, true); + if (!maxwatch) + maxwatch = 32; + } + + void Implements(char* List) + { + List[I_OnRehash] = List[I_OnGarbageCollect] = List[I_OnCleanup] = List[I_OnUserQuit] = List[I_OnPostConnect] = List[I_OnUserPostNick] = List[I_On005Numeric] = 1; + } + + virtual void OnUserQuit(userrec* user, const std::string &reason, const std::string &oper_message) + { + watchentries::iterator x = whos_watching_me->find(user->nick); + if (x != whos_watching_me->end()) + { + for (std::deque<userrec*>::iterator n = x->second.begin(); n != x->second.end(); n++) + { + if (!user->Visibility || user->Visibility->VisibleTo(user)) + (*n)->WriteServ("601 %s %s %s %s %lu :went offline", (*n)->nick ,user->nick, user->ident, user->dhost, ServerInstance->Time()); + + watchlist* wl; + if ((*n)->GetExt("watchlist", wl)) + /* We were on somebody's notify list, set ourselves offline */ + (*wl)[user->nick] = ""; + } + } + + /* Now im quitting, if i have a notify list, im no longer watching anyone */ + watchlist* wl; + if (user->GetExt("watchlist", wl)) + { + /* Iterate every user on my watch list, and take me out of the whos_watching_me map for each one we're watching */ + for (watchlist::iterator i = wl->begin(); i != wl->end(); i++) + { + watchentries::iterator x = whos_watching_me->find(i->first); + if (x != whos_watching_me->end()) + { + /* People are watching this user, am i one of them? */ + std::deque<userrec*>::iterator n = std::find(x->second.begin(), x->second.end(), user); + if (n != x->second.end()) + /* I'm no longer watching you... */ + x->second.erase(n); + + if (!x->second.size()) + whos_watching_me->erase(user->nick); + } + } + + /* User's quitting, we're done with this. */ + delete wl; + } + } + + virtual void OnGarbageCollect() + { + watchentries* old_watch = whos_watching_me; + whos_watching_me = new watchentries(); + + for (watchentries::const_iterator n = old_watch->begin(); n != old_watch->end(); n++) + whos_watching_me->insert(*n); + + delete old_watch; + } + + virtual void OnCleanup(int target_type, void* item) + { + if (target_type == TYPE_USER) + { + watchlist* wl; + userrec* user = (userrec*)item; + + if (user->GetExt("watchlist", wl)) + { + user->Shrink("watchlist"); + delete wl; + } + } + } + + virtual void OnPostConnect(userrec* user) + { + watchentries::iterator x = whos_watching_me->find(user->nick); + if (x != whos_watching_me->end()) + { + for (std::deque<userrec*>::iterator n = x->second.begin(); n != x->second.end(); n++) + { + if (!user->Visibility || user->Visibility->VisibleTo(user)) + (*n)->WriteServ("600 %s %s %s %s %lu :arrived online", (*n)->nick, user->nick, user->ident, user->dhost, user->age); + + watchlist* wl; + if ((*n)->GetExt("watchlist", wl)) + /* We were on somebody's notify list, set ourselves online */ + (*wl)[user->nick] = std::string(user->ident).append(" ").append(user->dhost).append(" ").append(ConvToStr(user->age)); + } + } + } + + virtual void OnUserPostNick(userrec* user, const std::string &oldnick) + { + watchentries::iterator new_online = whos_watching_me->find(user->nick); + watchentries::iterator new_offline = whos_watching_me->find(assign(oldnick)); + + if (new_online != whos_watching_me->end()) + { + for (std::deque<userrec*>::iterator n = new_online->second.begin(); n != new_online->second.end(); n++) + { + watchlist* wl; + if ((*n)->GetExt("watchlist", wl)) + { + (*wl)[user->nick] = std::string(user->ident).append(" ").append(user->dhost).append(" ").append(ConvToStr(user->age)); + if (!user->Visibility || user->Visibility->VisibleTo(user)) + (*n)->WriteServ("600 %s %s %s :arrived online", (*n)->nick, user->nick, (*wl)[user->nick].c_str()); + } + } + } + + if (new_offline != whos_watching_me->end()) + { + for (std::deque<userrec*>::iterator n = new_offline->second.begin(); n != new_offline->second.end(); n++) + { + watchlist* wl; + if ((*n)->GetExt("watchlist", wl)) + { + if (!user->Visibility || user->Visibility->VisibleTo(user)) + (*n)->WriteServ("601 %s %s %s %s %lu :went offline", (*n)->nick, oldnick.c_str(), user->ident, user->dhost, user->age); + (*wl)[oldnick.c_str()] = ""; + } + } + } + } + + virtual void On005Numeric(std::string &output) + { + // we don't really have a limit... + output = output + " WATCH=" + ConvToStr(maxwatch); + } + + virtual ~Modulewatch() + { + delete whos_watching_me; + } + + virtual Version GetVersion() + { + return Version(1,1,0,1,VF_VENDOR,API_VERSION); + } +}; + +MODULE_INIT(Modulewatch) + diff --git a/src/modules/m_xmlsocket.cpp b/src/modules/m_xmlsocket.cpp index 12da3762e..7f37f66c9 100644 --- a/src/modules/m_xmlsocket.cpp +++ b/src/modules/m_xmlsocket.cpp @@ -1 +1,170 @@ -/* +------------------------------------+
* | 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 "users.h"
#include "channels.h"
#include "modules.h"
#include "hashcomp.h"
/* $ModDesc: Provides XMLSocket support for clients */
class ModuleXMLSocket : public Module
{
ConfigReader* Conf;
std::vector<int> listenports;
public:
ModuleXMLSocket(InspIRCd* Me)
: Module(Me)
{
OnRehash(NULL,"");
}
virtual void OnRehash(userrec* user, const std::string ¶m)
{
Conf = new ConfigReader(ServerInstance);
for (unsigned int i = 0; i < listenports.size(); i++)
{
ServerInstance->Config->DelIOHook(listenports[i]);
}
listenports.clear();
for (int i = 0; i < Conf->Enumerate("bind"); i++)
{
// For each <bind> tag
std::string x = Conf->ReadValue("bind", "type", i);
if (((x.empty()) || (x == "clients")) && (Conf->ReadFlag("bind", "xmlsocket", i)))
{
// Get the port we're meant to be listening on with SSL
std::string port = Conf->ReadValue("bind", "port", i);
irc::portparser portrange(port, false);
long portno = -1;
while ((portno = portrange.GetToken()))
{
try
{
if (ServerInstance->Config->AddIOHook(portno, this))
{
listenports.push_back(portno);
for (size_t i = 0; i < ServerInstance->Config->ports.size(); i++)
if (ServerInstance->Config->ports[i]->GetPort() == portno)
ServerInstance->Config->ports[i]->SetDescription("xml");
}
else
{
ServerInstance->Log(DEFAULT, "m_xmlsocket.so: FAILED to enable XMLSocket on port %d, maybe you have another similar module loaded?", portno);
}
}
catch (ModuleException &e)
{
ServerInstance->Log(DEFAULT, "m_xmlsocket.so: FAILED to enable XMLSocket on port %d: %s. Maybe it's already hooked by the same port on a different IP, or you have another similar module loaded?", portno, e.GetReason());
}
}
}
}
DELETE(Conf);
}
virtual ~ModuleXMLSocket()
{
}
virtual void OnUnloadModule(Module* mod, const std::string &name)
{
if (mod == this)
{
for(unsigned int i = 0; i < listenports.size(); i++)
{
ServerInstance->Config->DelIOHook(listenports[i]);
for (size_t j = 0; j < ServerInstance->Config->ports.size(); j++)
if (ServerInstance->Config->ports[j]->GetPort() == listenports[i])
ServerInstance->Config->ports[j]->SetDescription("plaintext");
}
}
}
virtual Version GetVersion()
{
return Version(1, 1, 0, 0, VF_VENDOR, API_VERSION);
}
void Implements(char* List)
{
List[I_OnUnloadModule] = List[I_OnRawSocketRead] = List[I_OnRawSocketWrite] = List[I_OnRehash] = 1;
}
virtual int OnRawSocketRead(int fd, char* buffer, unsigned int count, int &readresult)
{
userrec* user = dynamic_cast<userrec*>(ServerInstance->FindDescriptor(fd));
if (user == NULL)
return -1;
int result = user->ReadData(buffer, count);
if ((result == -1) && (errno == EAGAIN))
return -1;
else if (result < 1)
return 0;
/* XXX: The core is more than happy to split lines purely on an \n
* rather than a \r\n. This is good for us as it means that the size
* of data we are receiving is exactly the same as the size of data
* we asked for, and we dont need to re-implement our own socket
* buffering (See below)
*/
for (int n = 0; n < result; n++)
if (buffer[n] == 0)
buffer[n] = '\n';
readresult = result;
return result;
}
virtual int OnRawSocketWrite(int fd, const char* buffer, int count)
{
userrec* user = dynamic_cast<userrec*>(ServerInstance->FindDescriptor(fd));
if (user == NULL)
return -1;
/* We want to alter the buffer, so we have to make a copy */
char * tmpbuffer = new char[count + 1];
memcpy(tmpbuffer, buffer, count);
/* XXX: This will actually generate lines "looking\0\0like\0\0this"
* rather than lines "looking\0like\0this". This shouldnt be a problem
* to the client, but it saves us a TON of processing and the need
* to re-implement socket buffering, as the data we are sending is
* exactly the same length as the data we are receiving.
*/
for (int n = 0; n < count; n++)
if ((tmpbuffer[n] == '\r') || (tmpbuffer[n] == '\n'))
tmpbuffer[n] = 0;
user->AddWriteBuf(std::string(tmpbuffer,count));
delete [] tmpbuffer;
return 1;
}
};
MODULE_INIT(ModuleXMLSocket)
\ No newline at end of file +/* +------------------------------------+ + * | 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 "users.h" +#include "channels.h" +#include "modules.h" +#include "hashcomp.h" + +/* $ModDesc: Provides XMLSocket support for clients */ + +class ModuleXMLSocket : public Module +{ + ConfigReader* Conf; + std::vector<int> listenports; + + public: + + ModuleXMLSocket(InspIRCd* Me) + : Module(Me) + { + OnRehash(NULL,""); + } + + virtual void OnRehash(userrec* user, const std::string ¶m) + { + + Conf = new ConfigReader(ServerInstance); + + for (unsigned int i = 0; i < listenports.size(); i++) + { + ServerInstance->Config->DelIOHook(listenports[i]); + } + + listenports.clear(); + + for (int i = 0; i < Conf->Enumerate("bind"); i++) + { + // For each <bind> tag + std::string x = Conf->ReadValue("bind", "type", i); + if (((x.empty()) || (x == "clients")) && (Conf->ReadFlag("bind", "xmlsocket", i))) + { + // Get the port we're meant to be listening on with SSL + std::string port = Conf->ReadValue("bind", "port", i); + irc::portparser portrange(port, false); + long portno = -1; + while ((portno = portrange.GetToken())) + { + try + { + if (ServerInstance->Config->AddIOHook(portno, this)) + { + listenports.push_back(portno); + for (size_t i = 0; i < ServerInstance->Config->ports.size(); i++) + if (ServerInstance->Config->ports[i]->GetPort() == portno) + ServerInstance->Config->ports[i]->SetDescription("xml"); + } + else + { + ServerInstance->Log(DEFAULT, "m_xmlsocket.so: FAILED to enable XMLSocket on port %d, maybe you have another similar module loaded?", portno); + } + } + catch (ModuleException &e) + { + ServerInstance->Log(DEFAULT, "m_xmlsocket.so: FAILED to enable XMLSocket on port %d: %s. Maybe it's already hooked by the same port on a different IP, or you have another similar module loaded?", portno, e.GetReason()); + } + } + } + } + + DELETE(Conf); + } + + virtual ~ModuleXMLSocket() + { + } + + virtual void OnUnloadModule(Module* mod, const std::string &name) + { + if (mod == this) + { + for(unsigned int i = 0; i < listenports.size(); i++) + { + ServerInstance->Config->DelIOHook(listenports[i]); + for (size_t j = 0; j < ServerInstance->Config->ports.size(); j++) + if (ServerInstance->Config->ports[j]->GetPort() == listenports[i]) + ServerInstance->Config->ports[j]->SetDescription("plaintext"); + } + } + } + + virtual Version GetVersion() + { + return Version(1, 1, 0, 0, VF_VENDOR, API_VERSION); + } + + void Implements(char* List) + { + List[I_OnUnloadModule] = List[I_OnRawSocketRead] = List[I_OnRawSocketWrite] = List[I_OnRehash] = 1; + } + + virtual int OnRawSocketRead(int fd, char* buffer, unsigned int count, int &readresult) + { + userrec* user = dynamic_cast<userrec*>(ServerInstance->FindDescriptor(fd)); + + if (user == NULL) + return -1; + + int result = user->ReadData(buffer, count); + + if ((result == -1) && (errno == EAGAIN)) + return -1; + else if (result < 1) + return 0; + + /* XXX: The core is more than happy to split lines purely on an \n + * rather than a \r\n. This is good for us as it means that the size + * of data we are receiving is exactly the same as the size of data + * we asked for, and we dont need to re-implement our own socket + * buffering (See below) + */ + for (int n = 0; n < result; n++) + if (buffer[n] == 0) + buffer[n] = '\n'; + + readresult = result; + return result; + } + + virtual int OnRawSocketWrite(int fd, const char* buffer, int count) + { + userrec* user = dynamic_cast<userrec*>(ServerInstance->FindDescriptor(fd)); + + if (user == NULL) + return -1; + + /* We want to alter the buffer, so we have to make a copy */ + char * tmpbuffer = new char[count + 1]; + memcpy(tmpbuffer, buffer, count); + + /* XXX: This will actually generate lines "looking\0\0like\0\0this" + * rather than lines "looking\0like\0this". This shouldnt be a problem + * to the client, but it saves us a TON of processing and the need + * to re-implement socket buffering, as the data we are sending is + * exactly the same length as the data we are receiving. + */ + for (int n = 0; n < count; n++) + if ((tmpbuffer[n] == '\r') || (tmpbuffer[n] == '\n')) + tmpbuffer[n] = 0; + + user->AddWriteBuf(std::string(tmpbuffer,count)); + delete [] tmpbuffer; + + return 1; + } + +}; + +MODULE_INIT(ModuleXMLSocket) + diff --git a/src/modules/transport.h b/src/modules/transport.h index d92cf0376..ba8e3973b 100644 --- a/src/modules/transport.h +++ b/src/modules/transport.h @@ -1 +1,231 @@ -/* +------------------------------------+
* | 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.
*
* ---------------------------------------------------
*/
#ifndef __TRANSPORT_H__
#define __TRANSPORT_H__
#include <map>
#include <string>
/** A generic container for certificate data
*/
typedef std::map<std::string,std::string> ssl_data;
/** A shorthand way of representing an iterator into ssl_data
*/
typedef ssl_data::iterator ssl_data_iter;
/** ssl_cert is a class which abstracts SSL certificate
* and key information.
*
* Because gnutls and openssl represent key information in
* wildly different ways, this class allows it to be accessed
* in a unified manner. These classes are attached to ssl-
* connected local users using Extensible::Extend() and the
* key 'ssl_cert'.
*/
class ssl_cert
{
/** Always contains an empty string
*/
const std::string empty;
public:
/** The data for this certificate
*/
ssl_data data;
/** Default constructor, initializes 'empty'
*/
ssl_cert() : empty("")
{
}
/** Get certificate distinguished name
* @return Certificate DN
*/
const std::string& GetDN()
{
ssl_data_iter ssldi = data.find("dn");
if (ssldi != data.end())
return ssldi->second;
else
return empty;
}
/** Get Certificate issuer
* @return Certificate issuer
*/
const std::string& GetIssuer()
{
ssl_data_iter ssldi = data.find("issuer");
if (ssldi != data.end())
return ssldi->second;
else
return empty;
}
/** Get error string if an error has occured
* @return The error associated with this users certificate,
* or an empty string if there is no error.
*/
const std::string& GetError()
{
ssl_data_iter ssldi = data.find("error");
if (ssldi != data.end())
return ssldi->second;
else
return empty;
}
/** Get key fingerprint.
* @return The key fingerprint as a hex string.
*/
const std::string& GetFingerprint()
{
ssl_data_iter ssldi = data.find("fingerprint");
if (ssldi != data.end())
return ssldi->second;
else
return empty;
}
/** Get trust status
* @return True if this is a trusted certificate
* (the certificate chain validates)
*/
bool IsTrusted()
{
ssl_data_iter ssldi = data.find("trusted");
if (ssldi != data.end())
return (ssldi->second == "1");
else
return false;
}
/** Get validity status
* @return True if the certificate itself is
* correctly formed.
*/
bool IsInvalid()
{
ssl_data_iter ssldi = data.find("invalid");
if (ssldi != data.end())
return (ssldi->second == "1");
else
return false;
}
/** Get signer status
* @return True if the certificate appears to be
* self-signed.
*/
bool IsUnknownSigner()
{
ssl_data_iter ssldi = data.find("unknownsigner");
if (ssldi != data.end())
return (ssldi->second == "1");
else
return false;
}
/** Get revokation status.
* @return True if the certificate is revoked.
* Note that this only works properly for GnuTLS
* right now.
*/
bool IsRevoked()
{
ssl_data_iter ssldi = data.find("revoked");
if (ssldi != data.end())
return (ssldi->second == "1");
else
return false;
}
};
/** Used to represent a request to a transport provider module
*/
class ISHRequest : public Request
{
public:
InspSocket* Sock;
ISHRequest(Module* Me, Module* Target, const char* rtype, InspSocket* sock) : Request(Me, Target, rtype), Sock(sock)
{
}
};
/** Used to represent a request to attach a cert to an InspSocket
*/
class InspSocketAttachCertRequest : public ISHRequest
{
public:
/** Initialize the request as an attach cert message */
InspSocketAttachCertRequest(InspSocket* is, Module* Me, Module* Target) : ISHRequest(Me, Target, "IS_ATTACH", is)
{
}
};
/** Used to check if a handshake is complete on an InspSocket yet
*/
class InspSocketHSCompleteRequest : public ISHRequest
{
public:
/** Initialize the request as a 'handshake complete?' message */
InspSocketHSCompleteRequest(InspSocket* is, Module* Me, Module* Target) : ISHRequest(Me, Target, "IS_HSDONE", is)
{
}
};
/** Used to hook a transport provider to an InspSocket
*/
class InspSocketHookRequest : public ISHRequest
{
public:
/** Initialize request as a hook message */
InspSocketHookRequest(InspSocket* is, Module* Me, Module* Target) : ISHRequest(Me, Target, "IS_HOOK", is)
{
}
};
/** Used to unhook a transport provider from an InspSocket
*/
class InspSocketUnhookRequest : public ISHRequest
{
public:
/** Initialize request as an unhook message */
InspSocketUnhookRequest(InspSocket* is, Module* Me, Module* Target) : ISHRequest(Me, Target, "IS_UNHOOK", is)
{
}
};
class InspSocketNameRequest : public ISHRequest
{
public:
/** Initialize request as a get name message */
InspSocketNameRequest(Module* Me, Module* Target) : ISHRequest(Me, Target, "IS_NAME", NULL)
{
}
};
#endif
\ No newline at end of file +/* +------------------------------------+ + * | 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. + * + * --------------------------------------------------- + */ + +#ifndef __TRANSPORT_H__ +#define __TRANSPORT_H__ + +#include <map> +#include <string> + +/** A generic container for certificate data + */ +typedef std::map<std::string,std::string> ssl_data; + +/** A shorthand way of representing an iterator into ssl_data + */ +typedef ssl_data::iterator ssl_data_iter; + +/** ssl_cert is a class which abstracts SSL certificate + * and key information. + * + * Because gnutls and openssl represent key information in + * wildly different ways, this class allows it to be accessed + * in a unified manner. These classes are attached to ssl- + * connected local users using Extensible::Extend() and the + * key 'ssl_cert'. + */ +class ssl_cert +{ + /** Always contains an empty string + */ + const std::string empty; + + public: + /** The data for this certificate + */ + ssl_data data; + + /** Default constructor, initializes 'empty' + */ + ssl_cert() : empty("") + { + } + + /** Get certificate distinguished name + * @return Certificate DN + */ + const std::string& GetDN() + { + ssl_data_iter ssldi = data.find("dn"); + + if (ssldi != data.end()) + return ssldi->second; + else + return empty; + } + + /** Get Certificate issuer + * @return Certificate issuer + */ + const std::string& GetIssuer() + { + ssl_data_iter ssldi = data.find("issuer"); + + if (ssldi != data.end()) + return ssldi->second; + else + return empty; + } + + /** Get error string if an error has occured + * @return The error associated with this users certificate, + * or an empty string if there is no error. + */ + const std::string& GetError() + { + ssl_data_iter ssldi = data.find("error"); + + if (ssldi != data.end()) + return ssldi->second; + else + return empty; + } + + /** Get key fingerprint. + * @return The key fingerprint as a hex string. + */ + const std::string& GetFingerprint() + { + ssl_data_iter ssldi = data.find("fingerprint"); + + if (ssldi != data.end()) + return ssldi->second; + else + return empty; + } + + /** Get trust status + * @return True if this is a trusted certificate + * (the certificate chain validates) + */ + bool IsTrusted() + { + ssl_data_iter ssldi = data.find("trusted"); + + if (ssldi != data.end()) + return (ssldi->second == "1"); + else + return false; + } + + /** Get validity status + * @return True if the certificate itself is + * correctly formed. + */ + bool IsInvalid() + { + ssl_data_iter ssldi = data.find("invalid"); + + if (ssldi != data.end()) + return (ssldi->second == "1"); + else + return false; + } + + /** Get signer status + * @return True if the certificate appears to be + * self-signed. + */ + bool IsUnknownSigner() + { + ssl_data_iter ssldi = data.find("unknownsigner"); + + if (ssldi != data.end()) + return (ssldi->second == "1"); + else + return false; + } + + /** Get revokation status. + * @return True if the certificate is revoked. + * Note that this only works properly for GnuTLS + * right now. + */ + bool IsRevoked() + { + ssl_data_iter ssldi = data.find("revoked"); + + if (ssldi != data.end()) + return (ssldi->second == "1"); + else + return false; + } +}; + +/** Used to represent a request to a transport provider module + */ +class ISHRequest : public Request +{ + public: + InspSocket* Sock; + + ISHRequest(Module* Me, Module* Target, const char* rtype, InspSocket* sock) : Request(Me, Target, rtype), Sock(sock) + { + } +}; + +/** Used to represent a request to attach a cert to an InspSocket + */ +class InspSocketAttachCertRequest : public ISHRequest +{ + public: + /** Initialize the request as an attach cert message */ + InspSocketAttachCertRequest(InspSocket* is, Module* Me, Module* Target) : ISHRequest(Me, Target, "IS_ATTACH", is) + { + } +}; + +/** Used to check if a handshake is complete on an InspSocket yet + */ +class InspSocketHSCompleteRequest : public ISHRequest +{ + public: + /** Initialize the request as a 'handshake complete?' message */ + InspSocketHSCompleteRequest(InspSocket* is, Module* Me, Module* Target) : ISHRequest(Me, Target, "IS_HSDONE", is) + { + } +}; + +/** Used to hook a transport provider to an InspSocket + */ +class InspSocketHookRequest : public ISHRequest +{ + public: + /** Initialize request as a hook message */ + InspSocketHookRequest(InspSocket* is, Module* Me, Module* Target) : ISHRequest(Me, Target, "IS_HOOK", is) + { + } +}; + +/** Used to unhook a transport provider from an InspSocket + */ +class InspSocketUnhookRequest : public ISHRequest +{ + public: + /** Initialize request as an unhook message */ + InspSocketUnhookRequest(InspSocket* is, Module* Me, Module* Target) : ISHRequest(Me, Target, "IS_UNHOOK", is) + { + } +}; + +class InspSocketNameRequest : public ISHRequest +{ + public: + /** Initialize request as a get name message */ + InspSocketNameRequest(Module* Me, Module* Target) : ISHRequest(Me, Target, "IS_NAME", NULL) + { + } +}; + +#endif + diff --git a/src/snomasks.cpp b/src/snomasks.cpp index 05571ce59..ba6d3d7d5 100644 --- a/src/snomasks.cpp +++ b/src/snomasks.cpp @@ -1 +1,102 @@ -/* +------------------------------------+
* | 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 <stdarg.h>
#include "configreader.h"
#include "users.h"
#include "snomasks.h"
SnomaskManager::SnomaskManager(InspIRCd* Instance) : ServerInstance(Instance)
{
SnoMasks.clear();
this->SetupDefaults();
}
SnomaskManager::~SnomaskManager()
{
}
bool SnomaskManager::EnableSnomask(char letter, const std::string &type)
{
if (SnoMasks.find(letter) == SnoMasks.end())
{
SnoMasks[letter] = type;
return true;
}
return false;
}
bool SnomaskManager::DisableSnomask(char letter)
{
SnoList::iterator n = SnoMasks.find(letter);
if (n != SnoMasks.end())
{
SnoMasks.erase(n);
return true;
}
return false;
}
void SnomaskManager::WriteToSnoMask(char letter, const std::string &text)
{
/* Only send to snomask chars which are enabled */
SnoList::iterator n = SnoMasks.find(letter);
if (n != SnoMasks.end())
{
/* Only opers can receive snotices, so we iterate the oper list */
for (std::vector<userrec*>::iterator i = ServerInstance->all_opers.begin(); i != ServerInstance->all_opers.end(); i++)
{
userrec* a = *i;
if (IS_LOCAL(a) && a->modes[UM_SERVERNOTICE] && a->modes[UM_SNOMASK] && a->IsNoticeMaskSet(n->first))
{
/* send server notices to all with +ns */
a->WriteServ("NOTICE %s :*** %s: %s",a->nick, n->second.c_str(), text.c_str());
}
}
}
}
void SnomaskManager::WriteToSnoMask(char letter, const char* text, ...)
{
char textbuffer[MAXBUF];
va_list argsPtr;
va_start(argsPtr, text);
vsnprintf(textbuffer, MAXBUF, text, argsPtr);
va_end(argsPtr);
this->WriteToSnoMask(letter, std::string(textbuffer));
}
bool SnomaskManager::IsEnabled(char letter)
{
return (SnoMasks.find(letter) != SnoMasks.end());
}
void SnomaskManager::SetupDefaults()
{
this->EnableSnomask('c',"CONNECT"); /* Local connect notices */
this->EnableSnomask('C',"REMOTECONNECT"); /* Remote connect notices */
this->EnableSnomask('q',"QUIT"); /* Local quit notices */
this->EnableSnomask('Q',"REMOTEQUIT"); /* Remote quit notices */
this->EnableSnomask('k',"KILL"); /* Kill notices */
this->EnableSnomask('K',"REMOTEKILL"); /* Remote kill notices */
this->EnableSnomask('l',"LINK"); /* Link notices */
this->EnableSnomask('o',"OPER"); /* Oper up/down notices */
this->EnableSnomask('d',"DEBUG"); /* Debug notices */
this->EnableSnomask('x',"XLINE"); /* Xline notice (g/z/q/k/e) */
this->EnableSnomask('t',"STATS"); /* Local or remote stats request */
this->EnableSnomask('f',"FLOOD"); /* Flooding notices */
}
\ No newline at end of file +/* +------------------------------------+ + * | 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 <stdarg.h> +#include "configreader.h" +#include "users.h" +#include "snomasks.h" + +SnomaskManager::SnomaskManager(InspIRCd* Instance) : ServerInstance(Instance) +{ + SnoMasks.clear(); + this->SetupDefaults(); +} + +SnomaskManager::~SnomaskManager() +{ +} + +bool SnomaskManager::EnableSnomask(char letter, const std::string &type) +{ + if (SnoMasks.find(letter) == SnoMasks.end()) + { + SnoMasks[letter] = type; + return true; + } + return false; +} + +bool SnomaskManager::DisableSnomask(char letter) +{ + SnoList::iterator n = SnoMasks.find(letter); + if (n != SnoMasks.end()) + { + SnoMasks.erase(n); + return true; + } + return false; +} + +void SnomaskManager::WriteToSnoMask(char letter, const std::string &text) +{ + /* Only send to snomask chars which are enabled */ + SnoList::iterator n = SnoMasks.find(letter); + if (n != SnoMasks.end()) + { + /* Only opers can receive snotices, so we iterate the oper list */ + for (std::vector<userrec*>::iterator i = ServerInstance->all_opers.begin(); i != ServerInstance->all_opers.end(); i++) + { + userrec* a = *i; + if (IS_LOCAL(a) && a->modes[UM_SERVERNOTICE] && a->modes[UM_SNOMASK] && a->IsNoticeMaskSet(n->first)) + { + /* send server notices to all with +ns */ + a->WriteServ("NOTICE %s :*** %s: %s",a->nick, n->second.c_str(), text.c_str()); + } + } + } +} + +void SnomaskManager::WriteToSnoMask(char letter, const char* text, ...) +{ + char textbuffer[MAXBUF]; + va_list argsPtr; + + va_start(argsPtr, text); + vsnprintf(textbuffer, MAXBUF, text, argsPtr); + va_end(argsPtr); + + this->WriteToSnoMask(letter, std::string(textbuffer)); +} + +bool SnomaskManager::IsEnabled(char letter) +{ + return (SnoMasks.find(letter) != SnoMasks.end()); +} + +void SnomaskManager::SetupDefaults() +{ + this->EnableSnomask('c',"CONNECT"); /* Local connect notices */ + this->EnableSnomask('C',"REMOTECONNECT"); /* Remote connect notices */ + this->EnableSnomask('q',"QUIT"); /* Local quit notices */ + this->EnableSnomask('Q',"REMOTEQUIT"); /* Remote quit notices */ + this->EnableSnomask('k',"KILL"); /* Kill notices */ + this->EnableSnomask('K',"REMOTEKILL"); /* Remote kill notices */ + this->EnableSnomask('l',"LINK"); /* Link notices */ + this->EnableSnomask('o',"OPER"); /* Oper up/down notices */ + this->EnableSnomask('d',"DEBUG"); /* Debug notices */ + this->EnableSnomask('x',"XLINE"); /* Xline notice (g/z/q/k/e) */ + this->EnableSnomask('t',"STATS"); /* Local or remote stats request */ + this->EnableSnomask('f',"FLOOD"); /* Flooding notices */ +} + diff --git a/src/socket.cpp b/src/socket.cpp index d400a0323..31fbffb61 100644 --- a/src/socket.cpp +++ b/src/socket.cpp @@ -1 +1,568 @@ -/* +------------------------------------+
* | 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 <string>
#include "configreader.h"
#include "socket.h"
#include "socketengine.h"
#include "wildcard.h"
using namespace irc::sockets;
/* Used when comparing CIDR masks for the modulus bits left over.
* A lot of ircd's seem to do this:
* ((-1) << (8 - (mask % 8)))
* But imho, it sucks in comparison to a nice neat lookup table.
*/
const unsigned char inverted_bits[8] = { 0x00, /* 00000000 - 0 bits - never actually used */
0x80, /* 10000000 - 1 bits */
0xC0, /* 11000000 - 2 bits */
0xE0, /* 11100000 - 3 bits */
0xF0, /* 11110000 - 4 bits */
0xF8, /* 11111000 - 5 bits */
0xFC, /* 11111100 - 6 bits */
0xFE /* 11111110 - 7 bits */
};
ListenSocket::ListenSocket(InspIRCd* Instance, int port, char* addr) : ServerInstance(Instance), desc("plaintext"), bind_addr(addr), bind_port(port)
{
this->SetFd(OpenTCPSocket(addr));
if (this->GetFd() > -1)
{
if (!Instance->BindSocket(this->fd,port,addr))
this->fd = -1;
#ifdef IPV6
if ((!*addr) || (strchr(addr,':')))
this->family = AF_INET6;
else
#endif
this->family = AF_INET;
Instance->SE->AddFd(this);
}
}
ListenSocket::~ListenSocket()
{
if (this->GetFd() > -1)
{
ServerInstance->SE->DelFd(this);
ServerInstance->Log(DEBUG,"Shut down listener on fd %d", this->fd);
if (shutdown(this->fd, 2) || close(this->fd))
ServerInstance->Log(DEBUG,"Failed to cancel listener: %s", strerror(errno));
this->fd = -1;
}
}
void ListenSocket::HandleEvent(EventType et, int errornum)
{
sockaddr* sock_us = new sockaddr[2]; // our port number
sockaddr* client = new sockaddr[2];
socklen_t uslen, length; // length of our port number
int incomingSockfd, in_port;
#ifdef IPV6
if (this->family == AF_INET6)
{
uslen = sizeof(sockaddr_in6);
length = sizeof(sockaddr_in6);
}
else
#endif
{
uslen = sizeof(sockaddr_in);
length = sizeof(sockaddr_in);
}
incomingSockfd = _accept (this->GetFd(), (sockaddr*)client, &length);
if ((incomingSockfd > -1) && (!_getsockname(incomingSockfd, sock_us, &uslen)))
{
char buf[MAXBUF];
#ifdef IPV6
if (this->family == AF_INET6)
{
inet_ntop(AF_INET6, &((const sockaddr_in6*)client)->sin6_addr, buf, sizeof(buf));
in_port = ntohs(((sockaddr_in6*)sock_us)->sin6_port);
}
else
#endif
{
inet_ntop(AF_INET, &((const sockaddr_in*)client)->sin_addr, buf, sizeof(buf));
in_port = ntohs(((sockaddr_in*)sock_us)->sin_port);
}
NonBlocking(incomingSockfd);
if (ServerInstance->Config->GetIOHook(in_port))
{
try
{
ServerInstance->Config->GetIOHook(in_port)->OnRawSocketAccept(incomingSockfd, buf, in_port);
}
catch (CoreException& modexcept)
{
ServerInstance->Log(DEBUG,"%s threw an exception: %s", modexcept.GetSource(), modexcept.GetReason());
}
}
ServerInstance->stats->statsAccept++;
userrec::AddClient(ServerInstance, incomingSockfd, in_port, false, this->family, client);
}
else
{
shutdown(incomingSockfd,2);
close(incomingSockfd);
ServerInstance->stats->statsRefused++;
}
delete[] client;
delete[] sock_us;
}
/* Match raw bytes using CIDR bit matching, used by higher level MatchCIDR() */
bool irc::sockets::MatchCIDRBits(unsigned char* address, unsigned char* mask, unsigned int mask_bits)
{
unsigned int modulus = mask_bits % 8; /* Number of whole bytes in the mask */
unsigned int divisor = mask_bits / 8; /* Remaining bits in the mask after whole bytes are dealt with */
/* First compare the whole bytes, if they dont match, return false */
if (memcmp(address, mask, divisor))
return false;
/* Now if there are any remainder bits, we compare them with logic AND */
if (modulus)
if ((address[divisor] & inverted_bits[modulus]) != (mask[divisor] & inverted_bits[modulus]))
/* If they dont match, return false */
return false;
/* The address matches the mask, to mask_bits bits of mask */
return true;
}
/* Match CIDR, but dont attempt to match() against leading *!*@ sections */
bool irc::sockets::MatchCIDR(const char* address, const char* cidr_mask)
{
return MatchCIDR(address, cidr_mask, false);
}
/* Match CIDR strings, e.g. 127.0.0.1 to 127.0.0.0/8 or 3ffe:1:5:6::8 to 3ffe:1::0/32
* If you have a lot of hosts to match, youre probably better off building your mask once
* and then using the lower level MatchCIDRBits directly.
*
* This will also attempt to match any leading usernames or nicknames on the mask, using
* match(), when match_with_username is true.
*/
bool irc::sockets::MatchCIDR(const char* address, const char* cidr_mask, bool match_with_username)
{
unsigned char addr_raw[16];
unsigned char mask_raw[16];
unsigned int bits = 0;
char* mask = NULL;
/* The caller is trying to match ident@<mask>/bits.
* Chop off the ident@ portion, use match() on it
* seperately.
*/
if (match_with_username)
{
/* Duplicate the strings, and try to find the position
* of the @ symbol in each */
char* address_dupe = strdup(address);
char* cidr_dupe = strdup(cidr_mask);
/* Use strchr not strrchr, because its going to be nearer to the left */
char* username_mask_pos = strrchr(cidr_dupe, '@');
char* username_addr_pos = strrchr(address_dupe, '@');
/* Both strings have an @ symbol in them */
if (username_mask_pos && username_addr_pos)
{
/* Zero out the location of the @ symbol */
*username_mask_pos = *username_addr_pos = 0;
/* Try and match() the strings before the @
* symbols, and recursively call MatchCIDR without
* username matching enabled to match the host part.
*/
bool result = (match(address_dupe, cidr_dupe) && MatchCIDR(username_addr_pos + 1, username_mask_pos + 1, false));
/* Free the stuff we created */
free(address_dupe);
free(cidr_dupe);
/* Return a result */
return result;
}
else
{
/* One or both didnt have an @ in,
* just match as CIDR
*/
free(address_dupe);
free(cidr_dupe);
mask = strdup(cidr_mask);
}
}
else
{
/* Make a copy of the cidr mask string,
* we're going to change it
*/
mask = strdup(cidr_mask);
}
in_addr address_in4;
in_addr mask_in4;
/* Use strrchr for this, its nearer to the right */
char* bits_chars = strrchr(mask,'/');
if (bits_chars)
{
bits = atoi(bits_chars + 1);
*bits_chars = 0;
}
else
{
/* No 'number of bits' field! */
free(mask);
return false;
}
#ifdef SUPPORT_IP6LINKS
in6_addr address_in6;
in6_addr mask_in6;
if (inet_pton(AF_INET6, address, &address_in6) > 0)
{
if (inet_pton(AF_INET6, mask, &mask_in6) > 0)
{
memcpy(&addr_raw, &address_in6.s6_addr, 16);
memcpy(&mask_raw, &mask_in6.s6_addr, 16);
if (bits > 128)
bits = 128;
}
else
{
/* The address was valid ipv6, but the mask
* that goes with it wasnt.
*/
free(mask);
return false;
}
}
else
#endif
if (inet_pton(AF_INET, address, &address_in4) > 0)
{
if (inet_pton(AF_INET, mask, &mask_in4) > 0)
{
memcpy(&addr_raw, &address_in4.s_addr, 4);
memcpy(&mask_raw, &mask_in4.s_addr, 4);
if (bits > 32)
bits = 32;
}
else
{
/* The address was valid ipv4,
* but the mask that went with it wasnt.
*/
free(mask);
return false;
}
}
else
{
/* The address was neither ipv4 or ipv6 */
free(mask);
return false;
}
/* Low-level-match the bits in the raw data */
free(mask);
return MatchCIDRBits(addr_raw, mask_raw, bits);
}
void irc::sockets::Blocking(int s)
{
#ifndef WIN32
int flags = fcntl(s, F_GETFL, 0);
fcntl(s, F_SETFL, flags ^ O_NONBLOCK);
#else
unsigned long opt = 0;
ioctlsocket(s, FIONBIO, &opt);
#endif
}
void irc::sockets::NonBlocking(int s)
{
#ifndef WIN32
int flags = fcntl(s, F_GETFL, 0);
fcntl(s, F_SETFL, flags | O_NONBLOCK);
#else
unsigned long opt = 1;
ioctlsocket(s, FIONBIO, &opt);
#endif
}
/** This will bind a socket to a port. It works for UDP/TCP.
* It can only bind to IP addresses, if you wish to bind to hostnames
* you should first resolve them using class 'Resolver'.
*/
bool InspIRCd::BindSocket(int sockfd, int port, char* addr, bool dolisten)
{
/* We allocate 2 of these, because sockaddr_in6 is larger than sockaddr (ugh, hax) */
sockaddr* server = new sockaddr[2];
memset(server,0,sizeof(sockaddr)*2);
int ret, size;
if (*addr == '*')
*addr = 0;
#ifdef IPV6
if (*addr)
{
/* There is an address here. Is it ipv6? */
if (strchr(addr,':'))
{
/* Yes it is */
in6_addr addy;
if (inet_pton(AF_INET6, addr, &addy) < 1)
{
delete[] server;
return false;
}
((sockaddr_in6*)server)->sin6_family = AF_INET6;
memcpy(&(((sockaddr_in6*)server)->sin6_addr), &addy, sizeof(in6_addr));
((sockaddr_in6*)server)->sin6_port = htons(port);
size = sizeof(sockaddr_in6);
}
else
{
/* No, its not */
in_addr addy;
if (inet_pton(AF_INET, addr, &addy) < 1)
{
delete[] server;
return false;
}
((sockaddr_in*)server)->sin_family = AF_INET;
((sockaddr_in*)server)->sin_addr = addy;
((sockaddr_in*)server)->sin_port = htons(port);
size = sizeof(sockaddr_in);
}
}
else
{
if (port == -1)
{
/* Port -1: Means UDP IPV4 port binding - Special case
* used by DNS engine.
*/
((sockaddr_in*)server)->sin_family = AF_INET;
((sockaddr_in*)server)->sin_addr.s_addr = htonl(INADDR_ANY);
((sockaddr_in*)server)->sin_port = 0;
size = sizeof(sockaddr_in);
}
else
{
/* Theres no address here, default to ipv6 bind to all */
((sockaddr_in6*)server)->sin6_family = AF_INET6;
memset(&(((sockaddr_in6*)server)->sin6_addr), 0, sizeof(in6_addr));
((sockaddr_in6*)server)->sin6_port = htons(port);
size = sizeof(sockaddr_in6);
}
}
#else
/* If we aren't built with ipv6, the choice becomes simple */
((sockaddr_in*)server)->sin_family = AF_INET;
if (*addr)
{
/* There is an address here. */
in_addr addy;
if (inet_pton(AF_INET, addr, &addy) < 1)
{
delete[] server;
return false;
}
((sockaddr_in*)server)->sin_addr = addy;
}
else
{
/* Bind ipv4 to all */
((sockaddr_in*)server)->sin_addr.s_addr = htonl(INADDR_ANY);
}
/* Bind ipv4 port number */
((sockaddr_in*)server)->sin_port = htons(port);
size = sizeof(sockaddr_in);
#endif
ret = bind(sockfd, server, size);
delete[] server;
if (ret < 0)
{
return false;
}
else
{
if (dolisten)
{
if (listen(sockfd, Config->MaxConn) == -1)
{
this->Log(DEFAULT,"ERROR in listen(): %s",strerror(errno));
return false;
}
else
{
this->Log(DEBUG,"New socket binding for %d with listen: %s:%d", sockfd, addr, port);
NonBlocking(sockfd);
return true;
}
}
else
{
this->Log(DEBUG,"New socket binding for %d without listen: %s:%d", sockfd, addr, port);
return true;
}
}
}
// Open a TCP Socket
int irc::sockets::OpenTCPSocket(char* addr, int socktype)
{
int sockfd;
int on = 1;
struct linger linger = { 0 };
#ifdef IPV6
if (strchr(addr,':') || (!*addr))
sockfd = socket (PF_INET6, socktype, 0);
else
sockfd = socket (PF_INET, socktype, 0);
if (sockfd < 0)
#else
if ((sockfd = socket (PF_INET, socktype, 0)) < 0)
#endif
{
return ERROR;
}
else
{
setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (char*)&on, sizeof(on));
/* This is BSD compatible, setting l_onoff to 0 is *NOT* http://web.irc.org/mla/ircd-dev/msg02259.html */
linger.l_onoff = 1;
linger.l_linger = 1;
setsockopt(sockfd, SOL_SOCKET, SO_LINGER, (char*)&linger,sizeof(linger));
return (sockfd);
}
}
/* XXX: Probably belongs in class InspIRCd */
int InspIRCd::BindPorts(bool bail, int &ports_found, FailedPortList &failed_ports)
{
char configToken[MAXBUF], Addr[MAXBUF], Type[MAXBUF];
int bound = 0;
bool started_with_nothing = (Config->ports.size() == 0);
std::vector<std::pair<std::string, int> > old_ports;
/* XXX: Make a copy of the old ip/port pairs here */
for (std::vector<ListenSocket*>::iterator o = Config->ports.begin(); o != Config->ports.end(); ++o)
old_ports.push_back(make_pair((*o)->GetIP(), (*o)->GetPort()));
for (int count = 0; count < Config->ConfValueEnum(Config->config_data, "bind"); count++)
{
Config->ConfValue(Config->config_data, "bind", "port", count, configToken, MAXBUF);
Config->ConfValue(Config->config_data, "bind", "address", count, Addr, MAXBUF);
Config->ConfValue(Config->config_data, "bind", "type", count, Type, MAXBUF);
if ((!*Type) || (!strcmp(Type,"clients")))
{
irc::portparser portrange(configToken, false);
int portno = -1;
while ((portno = portrange.GetToken()))
{
if (*Addr == '*')
*Addr = 0;
bool skip = false;
for (std::vector<ListenSocket*>::iterator n = Config->ports.begin(); n != Config->ports.end(); ++n)
{
if (((*n)->GetIP() == Addr) && ((*n)->GetPort() == portno))
{
skip = true;
/* XXX: Here, erase from our copy of the list */
for (std::vector<std::pair<std::string, int> >::iterator k = old_ports.begin(); k != old_ports.end(); ++k)
{
if ((k->first == Addr) && (k->second == portno))
{
old_ports.erase(k);
break;
}
}
}
}
if (!skip)
{
ListenSocket* ll = new ListenSocket(this, portno, Addr);
if (ll->GetFd() > -1)
{
bound++;
Config->ports.push_back(ll);
}
else
{
failed_ports.push_back(std::make_pair(Addr, portno));
}
ports_found++;
}
}
}
}
/* XXX: Here, anything left in our copy list, close as removed */
if (!started_with_nothing)
{
for (size_t k = 0; k < old_ports.size(); ++k)
{
for (std::vector<ListenSocket*>::iterator n = Config->ports.begin(); n != Config->ports.end(); ++n)
{
if (((*n)->GetIP() == old_ports[k].first) && ((*n)->GetPort() == old_ports[k].second))
{
this->Log(DEFAULT,"Port binding %s:%d was removed from the config file, closing.", old_ports[k].first.c_str(), old_ports[k].second);
delete *n;
Config->ports.erase(n);
break;
}
}
}
}
return bound;
}
const char* irc::sockets::insp_ntoa(insp_inaddr n)
{
static char buf[1024];
inet_ntop(AF_FAMILY, &n, buf, sizeof(buf));
return buf;
}
int irc::sockets::insp_aton(const char* a, insp_inaddr* n)
{
return inet_pton(AF_FAMILY, a, n);
}
\ No newline at end of file +/* +------------------------------------+ + * | 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 <string> +#include "configreader.h" +#include "socket.h" +#include "socketengine.h" +#include "wildcard.h" + +using namespace irc::sockets; + +/* Used when comparing CIDR masks for the modulus bits left over. + * A lot of ircd's seem to do this: + * ((-1) << (8 - (mask % 8))) + * But imho, it sucks in comparison to a nice neat lookup table. + */ +const unsigned char inverted_bits[8] = { 0x00, /* 00000000 - 0 bits - never actually used */ + 0x80, /* 10000000 - 1 bits */ + 0xC0, /* 11000000 - 2 bits */ + 0xE0, /* 11100000 - 3 bits */ + 0xF0, /* 11110000 - 4 bits */ + 0xF8, /* 11111000 - 5 bits */ + 0xFC, /* 11111100 - 6 bits */ + 0xFE /* 11111110 - 7 bits */ +}; + + +ListenSocket::ListenSocket(InspIRCd* Instance, int port, char* addr) : ServerInstance(Instance), desc("plaintext"), bind_addr(addr), bind_port(port) +{ + this->SetFd(OpenTCPSocket(addr)); + if (this->GetFd() > -1) + { + if (!Instance->BindSocket(this->fd,port,addr)) + this->fd = -1; +#ifdef IPV6 + if ((!*addr) || (strchr(addr,':'))) + this->family = AF_INET6; + else +#endif + this->family = AF_INET; + Instance->SE->AddFd(this); + } +} + +ListenSocket::~ListenSocket() +{ + if (this->GetFd() > -1) + { + ServerInstance->SE->DelFd(this); + ServerInstance->Log(DEBUG,"Shut down listener on fd %d", this->fd); + if (shutdown(this->fd, 2) || close(this->fd)) + ServerInstance->Log(DEBUG,"Failed to cancel listener: %s", strerror(errno)); + this->fd = -1; + } +} + +void ListenSocket::HandleEvent(EventType et, int errornum) +{ + sockaddr* sock_us = new sockaddr[2]; // our port number + sockaddr* client = new sockaddr[2]; + socklen_t uslen, length; // length of our port number + int incomingSockfd, in_port; + +#ifdef IPV6 + if (this->family == AF_INET6) + { + uslen = sizeof(sockaddr_in6); + length = sizeof(sockaddr_in6); + } + else +#endif + { + uslen = sizeof(sockaddr_in); + length = sizeof(sockaddr_in); + } + + incomingSockfd = _accept (this->GetFd(), (sockaddr*)client, &length); + + if ((incomingSockfd > -1) && (!_getsockname(incomingSockfd, sock_us, &uslen))) + { + char buf[MAXBUF]; +#ifdef IPV6 + if (this->family == AF_INET6) + { + inet_ntop(AF_INET6, &((const sockaddr_in6*)client)->sin6_addr, buf, sizeof(buf)); + in_port = ntohs(((sockaddr_in6*)sock_us)->sin6_port); + } + else +#endif + { + inet_ntop(AF_INET, &((const sockaddr_in*)client)->sin_addr, buf, sizeof(buf)); + in_port = ntohs(((sockaddr_in*)sock_us)->sin_port); + } + + NonBlocking(incomingSockfd); + if (ServerInstance->Config->GetIOHook(in_port)) + { + try + { + ServerInstance->Config->GetIOHook(in_port)->OnRawSocketAccept(incomingSockfd, buf, in_port); + } + catch (CoreException& modexcept) + { + ServerInstance->Log(DEBUG,"%s threw an exception: %s", modexcept.GetSource(), modexcept.GetReason()); + } + } + ServerInstance->stats->statsAccept++; + userrec::AddClient(ServerInstance, incomingSockfd, in_port, false, this->family, client); + } + else + { + shutdown(incomingSockfd,2); + close(incomingSockfd); + ServerInstance->stats->statsRefused++; + } + delete[] client; + delete[] sock_us; +} + +/* Match raw bytes using CIDR bit matching, used by higher level MatchCIDR() */ +bool irc::sockets::MatchCIDRBits(unsigned char* address, unsigned char* mask, unsigned int mask_bits) +{ + unsigned int modulus = mask_bits % 8; /* Number of whole bytes in the mask */ + unsigned int divisor = mask_bits / 8; /* Remaining bits in the mask after whole bytes are dealt with */ + + /* First compare the whole bytes, if they dont match, return false */ + if (memcmp(address, mask, divisor)) + return false; + + /* Now if there are any remainder bits, we compare them with logic AND */ + if (modulus) + if ((address[divisor] & inverted_bits[modulus]) != (mask[divisor] & inverted_bits[modulus])) + /* If they dont match, return false */ + return false; + + /* The address matches the mask, to mask_bits bits of mask */ + return true; +} + +/* Match CIDR, but dont attempt to match() against leading *!*@ sections */ +bool irc::sockets::MatchCIDR(const char* address, const char* cidr_mask) +{ + return MatchCIDR(address, cidr_mask, false); +} + +/* Match CIDR strings, e.g. 127.0.0.1 to 127.0.0.0/8 or 3ffe:1:5:6::8 to 3ffe:1::0/32 + * If you have a lot of hosts to match, youre probably better off building your mask once + * and then using the lower level MatchCIDRBits directly. + * + * This will also attempt to match any leading usernames or nicknames on the mask, using + * match(), when match_with_username is true. + */ +bool irc::sockets::MatchCIDR(const char* address, const char* cidr_mask, bool match_with_username) +{ + unsigned char addr_raw[16]; + unsigned char mask_raw[16]; + unsigned int bits = 0; + char* mask = NULL; + + /* The caller is trying to match ident@<mask>/bits. + * Chop off the ident@ portion, use match() on it + * seperately. + */ + if (match_with_username) + { + /* Duplicate the strings, and try to find the position + * of the @ symbol in each */ + char* address_dupe = strdup(address); + char* cidr_dupe = strdup(cidr_mask); + + /* Use strchr not strrchr, because its going to be nearer to the left */ + char* username_mask_pos = strrchr(cidr_dupe, '@'); + char* username_addr_pos = strrchr(address_dupe, '@'); + + /* Both strings have an @ symbol in them */ + if (username_mask_pos && username_addr_pos) + { + /* Zero out the location of the @ symbol */ + *username_mask_pos = *username_addr_pos = 0; + + /* Try and match() the strings before the @ + * symbols, and recursively call MatchCIDR without + * username matching enabled to match the host part. + */ + bool result = (match(address_dupe, cidr_dupe) && MatchCIDR(username_addr_pos + 1, username_mask_pos + 1, false)); + + /* Free the stuff we created */ + free(address_dupe); + free(cidr_dupe); + + /* Return a result */ + return result; + } + else + { + /* One or both didnt have an @ in, + * just match as CIDR + */ + free(address_dupe); + free(cidr_dupe); + mask = strdup(cidr_mask); + } + } + else + { + /* Make a copy of the cidr mask string, + * we're going to change it + */ + mask = strdup(cidr_mask); + } + + in_addr address_in4; + in_addr mask_in4; + + + /* Use strrchr for this, its nearer to the right */ + char* bits_chars = strrchr(mask,'/'); + + if (bits_chars) + { + bits = atoi(bits_chars + 1); + *bits_chars = 0; + } + else + { + /* No 'number of bits' field! */ + free(mask); + return false; + } + +#ifdef SUPPORT_IP6LINKS + in6_addr address_in6; + in6_addr mask_in6; + + if (inet_pton(AF_INET6, address, &address_in6) > 0) + { + if (inet_pton(AF_INET6, mask, &mask_in6) > 0) + { + memcpy(&addr_raw, &address_in6.s6_addr, 16); + memcpy(&mask_raw, &mask_in6.s6_addr, 16); + + if (bits > 128) + bits = 128; + } + else + { + /* The address was valid ipv6, but the mask + * that goes with it wasnt. + */ + free(mask); + return false; + } + } + else +#endif + if (inet_pton(AF_INET, address, &address_in4) > 0) + { + if (inet_pton(AF_INET, mask, &mask_in4) > 0) + { + memcpy(&addr_raw, &address_in4.s_addr, 4); + memcpy(&mask_raw, &mask_in4.s_addr, 4); + + if (bits > 32) + bits = 32; + } + else + { + /* The address was valid ipv4, + * but the mask that went with it wasnt. + */ + free(mask); + return false; + } + } + else + { + /* The address was neither ipv4 or ipv6 */ + free(mask); + return false; + } + + /* Low-level-match the bits in the raw data */ + free(mask); + return MatchCIDRBits(addr_raw, mask_raw, bits); +} + +void irc::sockets::Blocking(int s) +{ +#ifndef WIN32 + int flags = fcntl(s, F_GETFL, 0); + fcntl(s, F_SETFL, flags ^ O_NONBLOCK); +#else + unsigned long opt = 0; + ioctlsocket(s, FIONBIO, &opt); +#endif +} + +void irc::sockets::NonBlocking(int s) +{ +#ifndef WIN32 + int flags = fcntl(s, F_GETFL, 0); + fcntl(s, F_SETFL, flags | O_NONBLOCK); +#else + unsigned long opt = 1; + ioctlsocket(s, FIONBIO, &opt); +#endif +} + +/** This will bind a socket to a port. It works for UDP/TCP. + * It can only bind to IP addresses, if you wish to bind to hostnames + * you should first resolve them using class 'Resolver'. + */ +bool InspIRCd::BindSocket(int sockfd, int port, char* addr, bool dolisten) +{ + /* We allocate 2 of these, because sockaddr_in6 is larger than sockaddr (ugh, hax) */ + sockaddr* server = new sockaddr[2]; + memset(server,0,sizeof(sockaddr)*2); + + int ret, size; + + if (*addr == '*') + *addr = 0; + +#ifdef IPV6 + if (*addr) + { + /* There is an address here. Is it ipv6? */ + if (strchr(addr,':')) + { + /* Yes it is */ + in6_addr addy; + if (inet_pton(AF_INET6, addr, &addy) < 1) + { + delete[] server; + return false; + } + + ((sockaddr_in6*)server)->sin6_family = AF_INET6; + memcpy(&(((sockaddr_in6*)server)->sin6_addr), &addy, sizeof(in6_addr)); + ((sockaddr_in6*)server)->sin6_port = htons(port); + size = sizeof(sockaddr_in6); + } + else + { + /* No, its not */ + in_addr addy; + if (inet_pton(AF_INET, addr, &addy) < 1) + { + delete[] server; + return false; + } + + ((sockaddr_in*)server)->sin_family = AF_INET; + ((sockaddr_in*)server)->sin_addr = addy; + ((sockaddr_in*)server)->sin_port = htons(port); + size = sizeof(sockaddr_in); + } + } + else + { + if (port == -1) + { + /* Port -1: Means UDP IPV4 port binding - Special case + * used by DNS engine. + */ + ((sockaddr_in*)server)->sin_family = AF_INET; + ((sockaddr_in*)server)->sin_addr.s_addr = htonl(INADDR_ANY); + ((sockaddr_in*)server)->sin_port = 0; + size = sizeof(sockaddr_in); + } + else + { + /* Theres no address here, default to ipv6 bind to all */ + ((sockaddr_in6*)server)->sin6_family = AF_INET6; + memset(&(((sockaddr_in6*)server)->sin6_addr), 0, sizeof(in6_addr)); + ((sockaddr_in6*)server)->sin6_port = htons(port); + size = sizeof(sockaddr_in6); + } + } +#else + /* If we aren't built with ipv6, the choice becomes simple */ + ((sockaddr_in*)server)->sin_family = AF_INET; + if (*addr) + { + /* There is an address here. */ + in_addr addy; + if (inet_pton(AF_INET, addr, &addy) < 1) + { + delete[] server; + return false; + } + ((sockaddr_in*)server)->sin_addr = addy; + } + else + { + /* Bind ipv4 to all */ + ((sockaddr_in*)server)->sin_addr.s_addr = htonl(INADDR_ANY); + } + /* Bind ipv4 port number */ + ((sockaddr_in*)server)->sin_port = htons(port); + size = sizeof(sockaddr_in); +#endif + ret = bind(sockfd, server, size); + delete[] server; + + if (ret < 0) + { + return false; + } + else + { + if (dolisten) + { + if (listen(sockfd, Config->MaxConn) == -1) + { + this->Log(DEFAULT,"ERROR in listen(): %s",strerror(errno)); + return false; + } + else + { + this->Log(DEBUG,"New socket binding for %d with listen: %s:%d", sockfd, addr, port); + NonBlocking(sockfd); + return true; + } + } + else + { + this->Log(DEBUG,"New socket binding for %d without listen: %s:%d", sockfd, addr, port); + return true; + } + } +} + +// Open a TCP Socket +int irc::sockets::OpenTCPSocket(char* addr, int socktype) +{ + int sockfd; + int on = 1; + struct linger linger = { 0 }; +#ifdef IPV6 + if (strchr(addr,':') || (!*addr)) + sockfd = socket (PF_INET6, socktype, 0); + else + sockfd = socket (PF_INET, socktype, 0); + if (sockfd < 0) +#else + if ((sockfd = socket (PF_INET, socktype, 0)) < 0) +#endif + { + return ERROR; + } + else + { + setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (char*)&on, sizeof(on)); + /* This is BSD compatible, setting l_onoff to 0 is *NOT* http://web.irc.org/mla/ircd-dev/msg02259.html */ + linger.l_onoff = 1; + linger.l_linger = 1; + setsockopt(sockfd, SOL_SOCKET, SO_LINGER, (char*)&linger,sizeof(linger)); + return (sockfd); + } +} + +/* XXX: Probably belongs in class InspIRCd */ +int InspIRCd::BindPorts(bool bail, int &ports_found, FailedPortList &failed_ports) +{ + char configToken[MAXBUF], Addr[MAXBUF], Type[MAXBUF]; + int bound = 0; + bool started_with_nothing = (Config->ports.size() == 0); + std::vector<std::pair<std::string, int> > old_ports; + + /* XXX: Make a copy of the old ip/port pairs here */ + for (std::vector<ListenSocket*>::iterator o = Config->ports.begin(); o != Config->ports.end(); ++o) + old_ports.push_back(make_pair((*o)->GetIP(), (*o)->GetPort())); + + for (int count = 0; count < Config->ConfValueEnum(Config->config_data, "bind"); count++) + { + Config->ConfValue(Config->config_data, "bind", "port", count, configToken, MAXBUF); + Config->ConfValue(Config->config_data, "bind", "address", count, Addr, MAXBUF); + Config->ConfValue(Config->config_data, "bind", "type", count, Type, MAXBUF); + + if ((!*Type) || (!strcmp(Type,"clients"))) + { + irc::portparser portrange(configToken, false); + int portno = -1; + while ((portno = portrange.GetToken())) + { + if (*Addr == '*') + *Addr = 0; + + bool skip = false; + for (std::vector<ListenSocket*>::iterator n = Config->ports.begin(); n != Config->ports.end(); ++n) + { + if (((*n)->GetIP() == Addr) && ((*n)->GetPort() == portno)) + { + skip = true; + /* XXX: Here, erase from our copy of the list */ + for (std::vector<std::pair<std::string, int> >::iterator k = old_ports.begin(); k != old_ports.end(); ++k) + { + if ((k->first == Addr) && (k->second == portno)) + { + old_ports.erase(k); + break; + } + } + } + } + if (!skip) + { + ListenSocket* ll = new ListenSocket(this, portno, Addr); + if (ll->GetFd() > -1) + { + bound++; + Config->ports.push_back(ll); + } + else + { + failed_ports.push_back(std::make_pair(Addr, portno)); + } + ports_found++; + } + } + } + } + + /* XXX: Here, anything left in our copy list, close as removed */ + if (!started_with_nothing) + { + for (size_t k = 0; k < old_ports.size(); ++k) + { + for (std::vector<ListenSocket*>::iterator n = Config->ports.begin(); n != Config->ports.end(); ++n) + { + if (((*n)->GetIP() == old_ports[k].first) && ((*n)->GetPort() == old_ports[k].second)) + { + this->Log(DEFAULT,"Port binding %s:%d was removed from the config file, closing.", old_ports[k].first.c_str(), old_ports[k].second); + delete *n; + Config->ports.erase(n); + break; + } + } + } + } + + return bound; +} + +const char* irc::sockets::insp_ntoa(insp_inaddr n) +{ + static char buf[1024]; + inet_ntop(AF_FAMILY, &n, buf, sizeof(buf)); + return buf; +} + +int irc::sockets::insp_aton(const char* a, insp_inaddr* n) +{ + return inet_pton(AF_FAMILY, a, n); +} + diff --git a/src/socketengine.cpp b/src/socketengine.cpp index 6a4e653db..48f7e11bf 100644 --- a/src/socketengine.cpp +++ b/src/socketengine.cpp @@ -1 +1,93 @@ -/* +------------------------------------+
* | 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 "socketengine.h"
int EventHandler::GetFd()
{
return this->fd;
}
void EventHandler::SetFd(int FD)
{
this->fd = FD;
}
bool EventHandler::Readable()
{
return true;
}
bool EventHandler::Writeable()
{
return false;
}
void SocketEngine::WantWrite(EventHandler* eh)
{
}
SocketEngine::SocketEngine(InspIRCd* Instance) : ServerInstance(Instance)
{
memset(ref, 0, sizeof(ref));
}
SocketEngine::~SocketEngine()
{
}
bool SocketEngine::AddFd(EventHandler* eh)
{
return true;
}
bool SocketEngine::HasFd(int fd)
{
if ((fd < 0) || (fd > MAX_DESCRIPTORS))
return false;
return ref[fd];
}
EventHandler* SocketEngine::GetRef(int fd)
{
if ((fd < 0) || (fd > MAX_DESCRIPTORS))
return 0;
return ref[fd];
}
bool SocketEngine::DelFd(EventHandler* eh, bool force)
{
return true;
}
int SocketEngine::GetMaxFds()
{
return 0;
}
int SocketEngine::GetRemainingFds()
{
return 0;
}
int SocketEngine::DispatchEvents()
{
return 0;
}
std::string SocketEngine::GetName()
{
return "misconfigured";
}
\ No newline at end of file +/* +------------------------------------+ + * | 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 "socketengine.h" + +int EventHandler::GetFd() +{ + return this->fd; +} + +void EventHandler::SetFd(int FD) +{ + this->fd = FD; +} + +bool EventHandler::Readable() +{ + return true; +} + +bool EventHandler::Writeable() +{ + return false; +} + +void SocketEngine::WantWrite(EventHandler* eh) +{ +} + +SocketEngine::SocketEngine(InspIRCd* Instance) : ServerInstance(Instance) +{ + memset(ref, 0, sizeof(ref)); +} + +SocketEngine::~SocketEngine() +{ +} + +bool SocketEngine::AddFd(EventHandler* eh) +{ + return true; +} + +bool SocketEngine::HasFd(int fd) +{ + if ((fd < 0) || (fd > MAX_DESCRIPTORS)) + return false; + return ref[fd]; +} + +EventHandler* SocketEngine::GetRef(int fd) +{ + if ((fd < 0) || (fd > MAX_DESCRIPTORS)) + return 0; + return ref[fd]; +} + +bool SocketEngine::DelFd(EventHandler* eh, bool force) +{ + return true; +} + +int SocketEngine::GetMaxFds() +{ + return 0; +} + +int SocketEngine::GetRemainingFds() +{ + return 0; +} + +int SocketEngine::DispatchEvents() +{ + return 0; +} + +std::string SocketEngine::GetName() +{ + return "misconfigured"; +} + diff --git a/src/socketengine_epoll.cpp b/src/socketengine_epoll.cpp index 7a7f46d1b..4ed68ca57 100644 --- a/src/socketengine_epoll.cpp +++ b/src/socketengine_epoll.cpp @@ -1 +1,157 @@ -/* +------------------------------------+
* | 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 "exitcodes.h"
#include <sys/epoll.h>
#include "socketengine_epoll.h"
EPollEngine::EPollEngine(InspIRCd* Instance) : SocketEngine(Instance)
{
EngineHandle = epoll_create(MAX_DESCRIPTORS);
if (EngineHandle == -1)
{
ServerInstance->Log(SPARSE,"ERROR: Could not initialize socket engine: %s", strerror(errno));
ServerInstance->Log(SPARSE,"ERROR: Your kernel probably does not have the proper features. This is a fatal error, exiting now.");
printf("ERROR: Could not initialize socket engine: %s\n", strerror(errno));
printf("ERROR: Your kernel probably does not have the proper features. This is a fatal error, exiting now.\n");
InspIRCd::Exit(EXIT_STATUS_SOCKETENGINE);
}
CurrentSetSize = 0;
}
EPollEngine::~EPollEngine()
{
close(EngineHandle);
}
bool EPollEngine::AddFd(EventHandler* eh)
{
int fd = eh->GetFd();
if ((fd < 0) || (fd > MAX_DESCRIPTORS))
return false;
if (GetRemainingFds() <= 1)
return false;
if (ref[fd])
return false;
ref[fd] = eh;
struct epoll_event ev;
memset(&ev,0,sizeof(struct epoll_event));
eh->Readable() ? ev.events = EPOLLIN : ev.events = EPOLLOUT;
ev.data.fd = fd;
int i = epoll_ctl(EngineHandle, EPOLL_CTL_ADD, fd, &ev);
if (i < 0)
{
return false;
}
ServerInstance->Log(DEBUG,"New file descriptor: %d", fd);
CurrentSetSize++;
return true;
}
void EPollEngine::WantWrite(EventHandler* eh)
{
/** Use oneshot so that the system removes the writeable
* status for us and saves us a call.
*/
struct epoll_event ev;
memset(&ev,0,sizeof(struct epoll_event));
ev.events = EPOLLOUT;
ev.data.fd = eh->GetFd();
epoll_ctl(EngineHandle, EPOLL_CTL_MOD, eh->GetFd(), &ev);
}
bool EPollEngine::DelFd(EventHandler* eh, bool force)
{
int fd = eh->GetFd();
if ((fd < 0) || (fd > MAX_DESCRIPTORS))
return false;
struct epoll_event ev;
memset(&ev,0,sizeof(struct epoll_event));
eh->Readable() ? ev.events = EPOLLIN : ev.events = EPOLLOUT;
ev.data.fd = fd;
int i = epoll_ctl(EngineHandle, EPOLL_CTL_DEL, fd, &ev);
if (i < 0 && !force)
return false;
CurrentSetSize--;
ref[fd] = NULL;
ServerInstance->Log(DEBUG,"Remove file descriptor: %d", fd);
return true;
}
int EPollEngine::GetMaxFds()
{
return MAX_DESCRIPTORS;
}
int EPollEngine::GetRemainingFds()
{
return MAX_DESCRIPTORS - CurrentSetSize;
}
int EPollEngine::DispatchEvents()
{
socklen_t codesize;
int errcode;
int i = epoll_wait(EngineHandle, events, MAX_DESCRIPTORS, 1000);
for (int j = 0; j < i; j++)
{
if (events[j].events & EPOLLHUP)
{
if (ref[events[j].data.fd])
ref[events[j].data.fd]->HandleEvent(EVENT_ERROR, 0);
continue;
}
if (events[j].events & EPOLLERR)
{
/* Get error number */
if (getsockopt(events[j].data.fd, SOL_SOCKET, SO_ERROR, &errcode, &codesize) < 0)
errcode = errno;
if (ref[events[j].data.fd])
ref[events[j].data.fd]->HandleEvent(EVENT_ERROR, errcode);
continue;
}
if (events[j].events & EPOLLOUT)
{
struct epoll_event ev;
memset(&ev,0,sizeof(struct epoll_event));
ev.events = EPOLLIN;
ev.data.fd = events[j].data.fd;
epoll_ctl(EngineHandle, EPOLL_CTL_MOD, events[j].data.fd, &ev);
if (ref[events[j].data.fd])
ref[events[j].data.fd]->HandleEvent(EVENT_WRITE);
}
else
{
if (ref[events[j].data.fd])
ref[events[j].data.fd]->HandleEvent(EVENT_READ);
}
}
return i;
}
std::string EPollEngine::GetName()
{
return "epoll";
}
\ No newline at end of file +/* +------------------------------------+ + * | 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 "exitcodes.h" +#include <sys/epoll.h> +#include "socketengine_epoll.h" + +EPollEngine::EPollEngine(InspIRCd* Instance) : SocketEngine(Instance) +{ + EngineHandle = epoll_create(MAX_DESCRIPTORS); + + if (EngineHandle == -1) + { + ServerInstance->Log(SPARSE,"ERROR: Could not initialize socket engine: %s", strerror(errno)); + ServerInstance->Log(SPARSE,"ERROR: Your kernel probably does not have the proper features. This is a fatal error, exiting now."); + printf("ERROR: Could not initialize socket engine: %s\n", strerror(errno)); + printf("ERROR: Your kernel probably does not have the proper features. This is a fatal error, exiting now.\n"); + InspIRCd::Exit(EXIT_STATUS_SOCKETENGINE); + } + CurrentSetSize = 0; +} + +EPollEngine::~EPollEngine() +{ + close(EngineHandle); +} + +bool EPollEngine::AddFd(EventHandler* eh) +{ + int fd = eh->GetFd(); + if ((fd < 0) || (fd > MAX_DESCRIPTORS)) + return false; + + if (GetRemainingFds() <= 1) + return false; + + if (ref[fd]) + return false; + + ref[fd] = eh; + struct epoll_event ev; + memset(&ev,0,sizeof(struct epoll_event)); + eh->Readable() ? ev.events = EPOLLIN : ev.events = EPOLLOUT; + ev.data.fd = fd; + int i = epoll_ctl(EngineHandle, EPOLL_CTL_ADD, fd, &ev); + if (i < 0) + { + return false; + } + + ServerInstance->Log(DEBUG,"New file descriptor: %d", fd); + CurrentSetSize++; + return true; +} + +void EPollEngine::WantWrite(EventHandler* eh) +{ + /** Use oneshot so that the system removes the writeable + * status for us and saves us a call. + */ + struct epoll_event ev; + memset(&ev,0,sizeof(struct epoll_event)); + ev.events = EPOLLOUT; + ev.data.fd = eh->GetFd(); + epoll_ctl(EngineHandle, EPOLL_CTL_MOD, eh->GetFd(), &ev); +} + +bool EPollEngine::DelFd(EventHandler* eh, bool force) +{ + int fd = eh->GetFd(); + if ((fd < 0) || (fd > MAX_DESCRIPTORS)) + return false; + + struct epoll_event ev; + memset(&ev,0,sizeof(struct epoll_event)); + eh->Readable() ? ev.events = EPOLLIN : ev.events = EPOLLOUT; + ev.data.fd = fd; + int i = epoll_ctl(EngineHandle, EPOLL_CTL_DEL, fd, &ev); + + if (i < 0 && !force) + return false; + + CurrentSetSize--; + ref[fd] = NULL; + + ServerInstance->Log(DEBUG,"Remove file descriptor: %d", fd); + return true; +} + +int EPollEngine::GetMaxFds() +{ + return MAX_DESCRIPTORS; +} + +int EPollEngine::GetRemainingFds() +{ + return MAX_DESCRIPTORS - CurrentSetSize; +} + +int EPollEngine::DispatchEvents() +{ + socklen_t codesize; + int errcode; + int i = epoll_wait(EngineHandle, events, MAX_DESCRIPTORS, 1000); + for (int j = 0; j < i; j++) + { + if (events[j].events & EPOLLHUP) + { + if (ref[events[j].data.fd]) + ref[events[j].data.fd]->HandleEvent(EVENT_ERROR, 0); + continue; + } + if (events[j].events & EPOLLERR) + { + /* Get error number */ + if (getsockopt(events[j].data.fd, SOL_SOCKET, SO_ERROR, &errcode, &codesize) < 0) + errcode = errno; + if (ref[events[j].data.fd]) + ref[events[j].data.fd]->HandleEvent(EVENT_ERROR, errcode); + continue; + } + if (events[j].events & EPOLLOUT) + { + struct epoll_event ev; + memset(&ev,0,sizeof(struct epoll_event)); + ev.events = EPOLLIN; + ev.data.fd = events[j].data.fd; + epoll_ctl(EngineHandle, EPOLL_CTL_MOD, events[j].data.fd, &ev); + if (ref[events[j].data.fd]) + ref[events[j].data.fd]->HandleEvent(EVENT_WRITE); + } + else + { + if (ref[events[j].data.fd]) + ref[events[j].data.fd]->HandleEvent(EVENT_READ); + } + } + + return i; +} + +std::string EPollEngine::GetName() +{ + return "epoll"; +} + diff --git a/src/socketengine_iocp.cpp b/src/socketengine_iocp.cpp index 833bc097f..89fd8717f 100644 --- a/src/socketengine_iocp.cpp +++ b/src/socketengine_iocp.cpp @@ -1 +1,376 @@ -/* +------------------------------------+
* | 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 "socketengine_iocp.h"
#include <mswsock.h>
IOCPEngine::IOCPEngine(InspIRCd * Instance) : SocketEngine(Instance)
{
/* Create completion port */
m_completionPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, (ULONG_PTR)0, 0);
/* Null variables out. */
CurrentSetSize = 0;
EngineHandle = 0;
memset(ref, 0, sizeof(EventHandler*) * MAX_DESCRIPTORS);
}
IOCPEngine::~IOCPEngine()
{
CloseHandle(m_completionPort);
}
bool IOCPEngine::AddFd(EventHandler* eh)
{
int fake_fd = GenerateFd(eh->GetFd());
int is_accept = 0;
int opt_len = sizeof(int);
if(fake_fd < 0)
return false;
/* are we a listen socket? */
getsockopt(eh->GetFd(), SOL_SOCKET, SO_ACCEPTCONN, (char*)&is_accept, &opt_len);
/* set up the read event so the socket can actually receive data :P */
eh->m_internalFd = fake_fd;
eh->m_writeEvent = 0;
eh->m_acceptEvent = 0;
unsigned long completion_key = (ULONG_PTR)eh->m_internalFd;
/* assign the socket to the completion port */
if(!CreateIoCompletionPort((HANDLE)eh->GetFd(), m_completionPort, completion_key, 0))
return false;
/* set up binding, increase set size */
ref[fake_fd] = eh;
++CurrentSetSize;
/* setup initial events */
if(is_accept)
PostAcceptEvent(eh);
else
PostReadEvent(eh);
/* log message */
ServerInstance->Log(DEBUG, "New fake fd: %u, real fd: %u, address 0x%p", fake_fd, eh->GetFd(), eh);
/* post a write event if there is data to be written */
if(eh->Writeable())
WantWrite(eh);
/* we're all good =) */
try
{
m_binding.insert( map<int, EventHandler*>::value_type( eh->GetFd(), eh ) );
}
catch (...)
{
/* Ohshi-, map::insert failed :/ */
return false;
}
return true;
}
bool IOCPEngine::DelFd(EventHandler* eh, bool force /* = false */)
{
int fake_fd = eh->m_internalFd;
int fd = eh->GetFd();
if(ref[fake_fd] == 0)
return false;
ServerInstance->Log(DEBUG, "Removing fake fd %u, real fd %u, address 0x%p", fake_fd, eh->GetFd(), eh);
/* Cancel pending i/o operations. */
if (CancelIo((HANDLE)fd) == FALSE)
return false;
/* Free the buffer, and delete the event. */
if(eh->m_readEvent != 0)
{
if(((Overlapped*)eh->m_readEvent)->m_params != 0)
delete ((udp_overlap*)((Overlapped*)eh->m_readEvent)->m_params);
delete ((Overlapped*)eh->m_readEvent);
}
if(eh->m_writeEvent != 0)
delete ((Overlapped*)eh->m_writeEvent);
if(eh->m_acceptEvent != 0)
{
delete ((accept_overlap*)((Overlapped*)eh->m_acceptEvent)->m_params);
delete ((Overlapped*)eh->m_acceptEvent);
}
/* Clear binding */
ref[fake_fd] = 0;
m_binding.erase(eh->GetFd());
/* decrement set size */
--CurrentSetSize;
/* success */
return true;
}
void IOCPEngine::WantWrite(EventHandler* eh)
{
/* Post event - write begin */
if(!eh->m_writeEvent)
{
ULONG_PTR completion_key = (ULONG_PTR)eh->m_internalFd;
Overlapped * ov = new Overlapped(SOCKET_IO_EVENT_WRITE_READY, 0);
eh->m_writeEvent = (void*)ov;
PostQueuedCompletionStatus(m_completionPort, 0, completion_key, &ov->m_overlap);
}
}
bool IOCPEngine::PostCompletionEvent(EventHandler * eh, SocketIOEvent type, int param)
{
Overlapped * ov = new Overlapped(type, param);
ULONG_PTR completion_key = (ULONG_PTR)eh->m_internalFd;
return PostQueuedCompletionStatus(m_completionPort, 0, completion_key, &ov->m_overlap);
}
void IOCPEngine::PostReadEvent(EventHandler * eh)
{
Overlapped * ov = new Overlapped(SOCKET_IO_EVENT_READ_READY, 0);
DWORD flags = 0;
DWORD r_length = 0;
WSABUF buf;
/* by passing a null buffer pointer, we can have this working in the same way as epoll..
* its slower, but it saves modifying all network code.
*/
buf.buf = 0;
buf.len = 0;
/* determine socket type. */
DWORD sock_type;
int sock_len = sizeof(DWORD);
if(getsockopt(eh->GetFd(), SOL_SOCKET, SO_TYPE, (char*)&sock_type, &sock_len) == -1)
{
/* wtfhax? */
PostCompletionEvent(eh, SOCKET_IO_EVENT_ERROR, 0);
delete ov;
return;
}
switch(sock_type)
{
case SOCK_DGRAM: /* UDP Socket */
{
udp_overlap * uv = new udp_overlap;
uv->udp_sockaddr_len = sizeof(sockaddr);
buf.buf = (char*)uv->udp_buffer;
buf.len = sizeof(uv->udp_buffer);
ov->m_params = (unsigned long)uv;
if(WSARecvFrom(eh->GetFd(), &buf, 1, &uv->udp_len, &flags, uv->udp_sockaddr, (LPINT)&uv->udp_sockaddr_len, &ov->m_overlap, 0))
{
int err = WSAGetLastError();
if(err != WSA_IO_PENDING)
{
delete ov;
PostCompletionEvent(eh, SOCKET_IO_EVENT_ERROR, 0);
return;
}
}
}
break;
case SOCK_STREAM: /* TCP Socket */
{
if(WSARecv(eh->GetFd(), &buf, 1, &r_length, &flags, &ov->m_overlap, 0) == SOCKET_ERROR)
{
if(WSAGetLastError() != WSA_IO_PENDING)
{
delete ov;
PostCompletionEvent(eh, SOCKET_IO_EVENT_ERROR, 0);
return;
}
}
}
break;
default:
{
printf("unknwon socket type: %u\n", sock_type);
return;
}
break;
}
eh->m_readEvent = (void*)ov;
}
int IOCPEngine::DispatchEvents()
{
DWORD len;
LPOVERLAPPED overlap;
Overlapped * ov;
EventHandler * eh;
ULONG_PTR intfd;
int ret;
unsigned long bytes_recv;
while(GetQueuedCompletionStatus(m_completionPort, &len, &intfd, &overlap, 1000))
{
// woot, we got an event on a socket :P
eh = ref[intfd];
ov = CONTAINING_RECORD(overlap, Overlapped, m_overlap);
if(eh == 0) continue;
switch(ov->m_event)
{
case SOCKET_IO_EVENT_WRITE_READY:
{
eh->m_writeEvent = 0;
eh->HandleEvent(EVENT_WRITE, 0);
}
break;
case SOCKET_IO_EVENT_READ_READY:
{
if(ov->m_params)
{
// if we had params, it means we are a udp socket with a udp_overlap pointer in this long.
udp_overlap * uv = (udp_overlap*)ov->m_params;
uv->udp_len = len;
this->udp_ov = uv;
eh->m_readEvent = 0;
eh->HandleEvent(EVENT_READ, 0);
this->udp_ov = 0;
delete uv;
PostReadEvent(eh);
}
else
{
ret = ioctlsocket(eh->GetFd(), FIONREAD, &bytes_recv);
eh->m_readEvent = 0;
if(ret != 0 || bytes_recv == 0)
{
/* end of file */
PostCompletionEvent(eh, SOCKET_IO_EVENT_ERROR, EIO); /* Old macdonald had an error, EIEIO. */
}
else
{
eh->HandleEvent(EVENT_READ, 0);
PostReadEvent(eh);
}
}
}
break;
case SOCKET_IO_EVENT_ACCEPT:
{
/* this is kinda messy.. :/ */
eh->HandleEvent(EVENT_READ, ov->m_params);
delete ((accept_overlap*)ov->m_params);
eh->m_acceptEvent = 0;
PostAcceptEvent(eh);
}
break;
case SOCKET_IO_EVENT_ERROR:
{
eh->HandleEvent(EVENT_ERROR, ov->m_params);
}
break;
}
delete ov;
}
return 0;
}
void IOCPEngine::PostAcceptEvent(EventHandler * eh)
{
int fd = WSASocket(AF_INET, SOCK_STREAM, 0, 0, 0, WSA_FLAG_OVERLAPPED);
int len = sizeof(sockaddr_in) + 16;
DWORD dwBytes;
accept_overlap* ao = new accept_overlap;
memset(ao->buf, 0, 1024);
ao->socket = fd;
Overlapped* ov = new Overlapped(SOCKET_IO_EVENT_ACCEPT, (int)ao);
eh->m_acceptEvent = (void*)ov;
if(AcceptEx(eh->GetFd(), fd, ao->buf, 0, len, len, &dwBytes, &ov->m_overlap) == FALSE)
{
int err = WSAGetLastError();
if(err != WSA_IO_PENDING)
{
printf("PostAcceptEvent err: %d\n", err);
}
}
}
std::string IOCPEngine::GetName()
{
return "iocp";
}
int __accept_socket(SOCKET s, sockaddr * addr, int * addrlen, void * acceptevent)
{
Overlapped* ovl = (Overlapped*)acceptevent;
accept_overlap* ov = (accept_overlap*)ovl->m_params;
sockaddr_in* server_address = (sockaddr_in*)&ov->buf[10];
sockaddr_in* client_address = (sockaddr_in*)&ov->buf[38];
memcpy(addr, client_address, sizeof(sockaddr_in));
*addrlen = sizeof(sockaddr_in);
return ov->socket;
}
int __getsockname(SOCKET s, sockaddr * name, int * namelen, void * acceptevent)
{
Overlapped* ovl = (Overlapped*)acceptevent;
accept_overlap* ov = (accept_overlap*)ovl->m_params;
sockaddr_in* server_address = (sockaddr_in*)&ov->buf[10];
sockaddr_in* client_address = (sockaddr_in*)&ov->buf[38];
memcpy(name, server_address, sizeof(sockaddr_in));
*namelen = sizeof(sockaddr_in);
return 0;
}
int __recvfrom(SOCKET s, char * buf, int len, int flags, struct sockaddr * from, int * fromlen, udp_overlap * ov)
{
memcpy(buf, ov->udp_buffer, ov->udp_len);
memcpy(from, ov->udp_sockaddr, *fromlen);
return ov->udp_len;
}
EventHandler * IOCPEngine::GetRef(int fd)
{
map<int, EventHandler*>::iterator itr = m_binding.find(fd);
return (itr == m_binding.end()) ? 0 : itr->second;
}
bool IOCPEngine::HasFd(int fd)
{
return (GetRef(fd) != 0);
}
EventHandler * IOCPEngine::GetIntRef(int fd)
{
if(fd < 0 || fd > MAX_DESCRIPTORS)
return 0;
return ref[fd];
}
\ No newline at end of file +/* +------------------------------------+ + * | 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 "socketengine_iocp.h" +#include <mswsock.h> + +IOCPEngine::IOCPEngine(InspIRCd * Instance) : SocketEngine(Instance) +{ + /* Create completion port */ + m_completionPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, (ULONG_PTR)0, 0); + + /* Null variables out. */ + CurrentSetSize = 0; + EngineHandle = 0; + memset(ref, 0, sizeof(EventHandler*) * MAX_DESCRIPTORS); +} + +IOCPEngine::~IOCPEngine() +{ + CloseHandle(m_completionPort); +} + +bool IOCPEngine::AddFd(EventHandler* eh) +{ + int fake_fd = GenerateFd(eh->GetFd()); + int is_accept = 0; + int opt_len = sizeof(int); + if(fake_fd < 0) + return false; + + /* are we a listen socket? */ + getsockopt(eh->GetFd(), SOL_SOCKET, SO_ACCEPTCONN, (char*)&is_accept, &opt_len); + + /* set up the read event so the socket can actually receive data :P */ + eh->m_internalFd = fake_fd; + eh->m_writeEvent = 0; + eh->m_acceptEvent = 0; + + unsigned long completion_key = (ULONG_PTR)eh->m_internalFd; + /* assign the socket to the completion port */ + if(!CreateIoCompletionPort((HANDLE)eh->GetFd(), m_completionPort, completion_key, 0)) + return false; + + /* set up binding, increase set size */ + ref[fake_fd] = eh; + ++CurrentSetSize; + + /* setup initial events */ + if(is_accept) + PostAcceptEvent(eh); + else + PostReadEvent(eh); + + /* log message */ + ServerInstance->Log(DEBUG, "New fake fd: %u, real fd: %u, address 0x%p", fake_fd, eh->GetFd(), eh); + + /* post a write event if there is data to be written */ + if(eh->Writeable()) + WantWrite(eh); + + /* we're all good =) */ + try + { + m_binding.insert( map<int, EventHandler*>::value_type( eh->GetFd(), eh ) ); + } + catch (...) + { + /* Ohshi-, map::insert failed :/ */ + return false; + } + + return true; +} + +bool IOCPEngine::DelFd(EventHandler* eh, bool force /* = false */) +{ + int fake_fd = eh->m_internalFd; + int fd = eh->GetFd(); + + if(ref[fake_fd] == 0) + return false; + + ServerInstance->Log(DEBUG, "Removing fake fd %u, real fd %u, address 0x%p", fake_fd, eh->GetFd(), eh); + + /* Cancel pending i/o operations. */ + if (CancelIo((HANDLE)fd) == FALSE) + return false; + + /* Free the buffer, and delete the event. */ + if(eh->m_readEvent != 0) + { + if(((Overlapped*)eh->m_readEvent)->m_params != 0) + delete ((udp_overlap*)((Overlapped*)eh->m_readEvent)->m_params); + + delete ((Overlapped*)eh->m_readEvent); + } + + if(eh->m_writeEvent != 0) + delete ((Overlapped*)eh->m_writeEvent); + + if(eh->m_acceptEvent != 0) + { + delete ((accept_overlap*)((Overlapped*)eh->m_acceptEvent)->m_params); + delete ((Overlapped*)eh->m_acceptEvent); + } + + /* Clear binding */ + ref[fake_fd] = 0; + m_binding.erase(eh->GetFd()); + + /* decrement set size */ + --CurrentSetSize; + + /* success */ + return true; +} + +void IOCPEngine::WantWrite(EventHandler* eh) +{ + /* Post event - write begin */ + if(!eh->m_writeEvent) + { + ULONG_PTR completion_key = (ULONG_PTR)eh->m_internalFd; + Overlapped * ov = new Overlapped(SOCKET_IO_EVENT_WRITE_READY, 0); + eh->m_writeEvent = (void*)ov; + PostQueuedCompletionStatus(m_completionPort, 0, completion_key, &ov->m_overlap); + } +} + +bool IOCPEngine::PostCompletionEvent(EventHandler * eh, SocketIOEvent type, int param) +{ + Overlapped * ov = new Overlapped(type, param); + ULONG_PTR completion_key = (ULONG_PTR)eh->m_internalFd; + return PostQueuedCompletionStatus(m_completionPort, 0, completion_key, &ov->m_overlap); +} + +void IOCPEngine::PostReadEvent(EventHandler * eh) +{ + Overlapped * ov = new Overlapped(SOCKET_IO_EVENT_READ_READY, 0); + DWORD flags = 0; + DWORD r_length = 0; + WSABUF buf; + + /* by passing a null buffer pointer, we can have this working in the same way as epoll.. + * its slower, but it saves modifying all network code. + */ + buf.buf = 0; + buf.len = 0; + + /* determine socket type. */ + DWORD sock_type; + int sock_len = sizeof(DWORD); + if(getsockopt(eh->GetFd(), SOL_SOCKET, SO_TYPE, (char*)&sock_type, &sock_len) == -1) + { + /* wtfhax? */ + PostCompletionEvent(eh, SOCKET_IO_EVENT_ERROR, 0); + delete ov; + return; + } + switch(sock_type) + { + case SOCK_DGRAM: /* UDP Socket */ + { + udp_overlap * uv = new udp_overlap; + uv->udp_sockaddr_len = sizeof(sockaddr); + buf.buf = (char*)uv->udp_buffer; + buf.len = sizeof(uv->udp_buffer); + ov->m_params = (unsigned long)uv; + if(WSARecvFrom(eh->GetFd(), &buf, 1, &uv->udp_len, &flags, uv->udp_sockaddr, (LPINT)&uv->udp_sockaddr_len, &ov->m_overlap, 0)) + { + int err = WSAGetLastError(); + if(err != WSA_IO_PENDING) + { + delete ov; + PostCompletionEvent(eh, SOCKET_IO_EVENT_ERROR, 0); + return; + } + } + } + break; + + case SOCK_STREAM: /* TCP Socket */ + { + if(WSARecv(eh->GetFd(), &buf, 1, &r_length, &flags, &ov->m_overlap, 0) == SOCKET_ERROR) + { + if(WSAGetLastError() != WSA_IO_PENDING) + { + delete ov; + PostCompletionEvent(eh, SOCKET_IO_EVENT_ERROR, 0); + return; + } + } + } + break; + + default: + { + printf("unknwon socket type: %u\n", sock_type); + return; + } + break; + } + eh->m_readEvent = (void*)ov; +} + +int IOCPEngine::DispatchEvents() +{ + DWORD len; + LPOVERLAPPED overlap; + Overlapped * ov; + EventHandler * eh; + ULONG_PTR intfd; + int ret; + unsigned long bytes_recv; + + while(GetQueuedCompletionStatus(m_completionPort, &len, &intfd, &overlap, 1000)) + { + // woot, we got an event on a socket :P + eh = ref[intfd]; + ov = CONTAINING_RECORD(overlap, Overlapped, m_overlap); + if(eh == 0) continue; + switch(ov->m_event) + { + case SOCKET_IO_EVENT_WRITE_READY: + { + eh->m_writeEvent = 0; + eh->HandleEvent(EVENT_WRITE, 0); + } + break; + + case SOCKET_IO_EVENT_READ_READY: + { + if(ov->m_params) + { + // if we had params, it means we are a udp socket with a udp_overlap pointer in this long. + udp_overlap * uv = (udp_overlap*)ov->m_params; + uv->udp_len = len; + this->udp_ov = uv; + eh->m_readEvent = 0; + eh->HandleEvent(EVENT_READ, 0); + this->udp_ov = 0; + delete uv; + PostReadEvent(eh); + } + else + { + ret = ioctlsocket(eh->GetFd(), FIONREAD, &bytes_recv); + eh->m_readEvent = 0; + if(ret != 0 || bytes_recv == 0) + { + /* end of file */ + PostCompletionEvent(eh, SOCKET_IO_EVENT_ERROR, EIO); /* Old macdonald had an error, EIEIO. */ + } + else + { + eh->HandleEvent(EVENT_READ, 0); + PostReadEvent(eh); + } + } + } + break; + + case SOCKET_IO_EVENT_ACCEPT: + { + /* this is kinda messy.. :/ */ + eh->HandleEvent(EVENT_READ, ov->m_params); + delete ((accept_overlap*)ov->m_params); + eh->m_acceptEvent = 0; + PostAcceptEvent(eh); + } + break; + + case SOCKET_IO_EVENT_ERROR: + { + eh->HandleEvent(EVENT_ERROR, ov->m_params); + } + break; + } + + delete ov; + } + + return 0; +} + +void IOCPEngine::PostAcceptEvent(EventHandler * eh) +{ + int fd = WSASocket(AF_INET, SOCK_STREAM, 0, 0, 0, WSA_FLAG_OVERLAPPED); + int len = sizeof(sockaddr_in) + 16; + DWORD dwBytes; + accept_overlap* ao = new accept_overlap; + memset(ao->buf, 0, 1024); + ao->socket = fd; + + Overlapped* ov = new Overlapped(SOCKET_IO_EVENT_ACCEPT, (int)ao); + eh->m_acceptEvent = (void*)ov; + + if(AcceptEx(eh->GetFd(), fd, ao->buf, 0, len, len, &dwBytes, &ov->m_overlap) == FALSE) + { + int err = WSAGetLastError(); + if(err != WSA_IO_PENDING) + { + printf("PostAcceptEvent err: %d\n", err); + } + } +} + + +std::string IOCPEngine::GetName() +{ + return "iocp"; +} + +int __accept_socket(SOCKET s, sockaddr * addr, int * addrlen, void * acceptevent) +{ + Overlapped* ovl = (Overlapped*)acceptevent; + accept_overlap* ov = (accept_overlap*)ovl->m_params; + + sockaddr_in* server_address = (sockaddr_in*)&ov->buf[10]; + sockaddr_in* client_address = (sockaddr_in*)&ov->buf[38]; + + memcpy(addr, client_address, sizeof(sockaddr_in)); + *addrlen = sizeof(sockaddr_in); + + return ov->socket; +} + +int __getsockname(SOCKET s, sockaddr * name, int * namelen, void * acceptevent) +{ + Overlapped* ovl = (Overlapped*)acceptevent; + accept_overlap* ov = (accept_overlap*)ovl->m_params; + + sockaddr_in* server_address = (sockaddr_in*)&ov->buf[10]; + sockaddr_in* client_address = (sockaddr_in*)&ov->buf[38]; + + memcpy(name, server_address, sizeof(sockaddr_in)); + *namelen = sizeof(sockaddr_in); + + return 0; +} + +int __recvfrom(SOCKET s, char * buf, int len, int flags, struct sockaddr * from, int * fromlen, udp_overlap * ov) +{ + memcpy(buf, ov->udp_buffer, ov->udp_len); + memcpy(from, ov->udp_sockaddr, *fromlen); + return ov->udp_len; +} + +EventHandler * IOCPEngine::GetRef(int fd) +{ + map<int, EventHandler*>::iterator itr = m_binding.find(fd); + return (itr == m_binding.end()) ? 0 : itr->second; +} + +bool IOCPEngine::HasFd(int fd) +{ + return (GetRef(fd) != 0); +} + +EventHandler * IOCPEngine::GetIntRef(int fd) +{ + if(fd < 0 || fd > MAX_DESCRIPTORS) + return 0; + return ref[fd]; +} + diff --git a/src/socketengine_kqueue.cpp b/src/socketengine_kqueue.cpp index de9a78f4e..7fcdae2b6 100644 --- a/src/socketengine_kqueue.cpp +++ b/src/socketengine_kqueue.cpp @@ -1 +1,158 @@ -/* +------------------------------------+
* | 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 "exitcodes.h"
#include <sys/types.h>
#include <sys/event.h>
#include <sys/time.h>
#include "socketengine_kqueue.h"
KQueueEngine::KQueueEngine(InspIRCd* Instance) : SocketEngine(Instance)
{
EngineHandle = kqueue();
if (EngineHandle == -1)
{
ServerInstance->Log(SPARSE,"ERROR: Could not initialize socket engine. Your kernel probably does not have the proper features.");
ServerInstance->Log(SPARSE,"ERROR: this is a fatal error, exiting now.");
printf("ERROR: Could not initialize socket engine. Your kernel probably does not have the proper features.");
printf("ERROR: this is a fatal error, exiting now.");
InspIRCd::Exit(EXIT_STATUS_SOCKETENGINE);
}
CurrentSetSize = 0;
}
KQueueEngine::~KQueueEngine()
{
close(EngineHandle);
}
bool KQueueEngine::AddFd(EventHandler* eh)
{
int fd = eh->GetFd();
if ((fd < 0) || (fd > MAX_DESCRIPTORS))
return false;
if (GetRemainingFds() <= 1)
return false;
if (ref[fd])
return false;
ref[fd] = eh;
struct kevent ke;
EV_SET(&ke, fd, eh->Readable() ? EVFILT_READ : EVFILT_WRITE, EV_ADD, 0, 0, NULL);
int i = kevent(EngineHandle, &ke, 1, 0, 0, NULL);
if (i == -1)
return false;
CurrentSetSize++;
ServerInstance->Log(DEBUG,"New file descriptor: %d", fd);
return true;
}
bool KQueueEngine::DelFd(EventHandler* eh, bool force)
{
int fd = eh->GetFd();
if ((fd < 0) || (fd > MAX_DESCRIPTORS))
return false;
struct kevent ke;
EV_SET(&ke, eh->GetFd(), EVFILT_READ, EV_DELETE, 0, 0, NULL);
int i = kevent(EngineHandle, &ke, 1, 0, 0, NULL);
EV_SET(&ke, eh->GetFd(), EVFILT_WRITE, EV_DELETE, 0, 0, NULL);
int j = kevent(EngineHandle, &ke, 1, 0, 0, NULL);
if ((j < 0) && (i < 0) && !force)
return false;
CurrentSetSize--;
ref[fd] = NULL;
ServerInstance->Log(DEBUG,"Remove file descriptor: %d", fd);
return true;
}
void KQueueEngine::WantWrite(EventHandler* eh)
{
/** When changing an item in a kqueue, there is no 'modify' call
* as in epoll. Instead, we add the item again, and this overwrites
* the original setting rather than adding it twice. See man kqueue.
*/
struct kevent ke;
EV_SET(&ke, eh->GetFd(), EVFILT_WRITE, EV_ADD | EV_ONESHOT, 0, 0, NULL);
kevent(EngineHandle, &ke, 1, 0, 0, NULL);
}
int KQueueEngine::GetMaxFds()
{
return MAX_DESCRIPTORS;
}
int KQueueEngine::GetRemainingFds()
{
return MAX_DESCRIPTORS - CurrentSetSize;
}
int KQueueEngine::DispatchEvents()
{
ts.tv_nsec = 0;
ts.tv_sec = 1;
int i = kevent(EngineHandle, NULL, 0, &ke_list[0], MAX_DESCRIPTORS, &ts);
for (int j = 0; j < i; j++)
{
if (ke_list[j].flags & EV_EOF)
{
/* We love you kqueue, oh yes we do *sings*!
* kqueue gives us the error number directly in the EOF state!
* Unlike smelly epoll and select, where we have to getsockopt
* to get the error, this saves us time and cpu cycles. Go BSD!
*/
if (ref[ke_list[j].ident])
ref[ke_list[j].ident]->HandleEvent(EVENT_ERROR, ke_list[j].fflags);
continue;
}
if (ke_list[j].flags & EVFILT_WRITE)
{
/* This looks wrong but its right. As above, theres no modify
* call in kqueue. See the manpage.
*/
struct kevent ke;
EV_SET(&ke, ke_list[j].ident, EVFILT_READ, EV_ADD, 0, 0, NULL);
kevent(EngineHandle, &ke, 1, 0, 0, NULL);
if (ref[ke_list[j].ident])
ref[ke_list[j].ident]->HandleEvent(EVENT_WRITE);
}
else
{
if (ref[ke_list[j].ident])
ref[ke_list[j].ident]->HandleEvent(EVENT_READ);
}
}
return i;
}
std::string KQueueEngine::GetName()
{
return "kqueue";
}
\ No newline at end of file +/* +------------------------------------+ + * | 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 "exitcodes.h" +#include <sys/types.h> +#include <sys/event.h> +#include <sys/time.h> +#include "socketengine_kqueue.h" + + +KQueueEngine::KQueueEngine(InspIRCd* Instance) : SocketEngine(Instance) +{ + EngineHandle = kqueue(); + if (EngineHandle == -1) + { + ServerInstance->Log(SPARSE,"ERROR: Could not initialize socket engine. Your kernel probably does not have the proper features."); + ServerInstance->Log(SPARSE,"ERROR: this is a fatal error, exiting now."); + printf("ERROR: Could not initialize socket engine. Your kernel probably does not have the proper features."); + printf("ERROR: this is a fatal error, exiting now."); + InspIRCd::Exit(EXIT_STATUS_SOCKETENGINE); + } + CurrentSetSize = 0; +} + +KQueueEngine::~KQueueEngine() +{ + close(EngineHandle); +} + +bool KQueueEngine::AddFd(EventHandler* eh) +{ + int fd = eh->GetFd(); + + if ((fd < 0) || (fd > MAX_DESCRIPTORS)) + return false; + + if (GetRemainingFds() <= 1) + return false; + + if (ref[fd]) + return false; + + ref[fd] = eh; + + struct kevent ke; + EV_SET(&ke, fd, eh->Readable() ? EVFILT_READ : EVFILT_WRITE, EV_ADD, 0, 0, NULL); + + int i = kevent(EngineHandle, &ke, 1, 0, 0, NULL); + if (i == -1) + return false; + + CurrentSetSize++; + + ServerInstance->Log(DEBUG,"New file descriptor: %d", fd); + return true; +} + +bool KQueueEngine::DelFd(EventHandler* eh, bool force) +{ + int fd = eh->GetFd(); + + if ((fd < 0) || (fd > MAX_DESCRIPTORS)) + return false; + + struct kevent ke; + EV_SET(&ke, eh->GetFd(), EVFILT_READ, EV_DELETE, 0, 0, NULL); + + int i = kevent(EngineHandle, &ke, 1, 0, 0, NULL); + + EV_SET(&ke, eh->GetFd(), EVFILT_WRITE, EV_DELETE, 0, 0, NULL); + + int j = kevent(EngineHandle, &ke, 1, 0, 0, NULL); + + if ((j < 0) && (i < 0) && !force) + return false; + + CurrentSetSize--; + ref[fd] = NULL; + + ServerInstance->Log(DEBUG,"Remove file descriptor: %d", fd); + return true; +} + +void KQueueEngine::WantWrite(EventHandler* eh) +{ + /** When changing an item in a kqueue, there is no 'modify' call + * as in epoll. Instead, we add the item again, and this overwrites + * the original setting rather than adding it twice. See man kqueue. + */ + struct kevent ke; + EV_SET(&ke, eh->GetFd(), EVFILT_WRITE, EV_ADD | EV_ONESHOT, 0, 0, NULL); + kevent(EngineHandle, &ke, 1, 0, 0, NULL); +} + +int KQueueEngine::GetMaxFds() +{ + return MAX_DESCRIPTORS; +} + +int KQueueEngine::GetRemainingFds() +{ + return MAX_DESCRIPTORS - CurrentSetSize; +} + +int KQueueEngine::DispatchEvents() +{ + ts.tv_nsec = 0; + ts.tv_sec = 1; + int i = kevent(EngineHandle, NULL, 0, &ke_list[0], MAX_DESCRIPTORS, &ts); + for (int j = 0; j < i; j++) + { + if (ke_list[j].flags & EV_EOF) + { + /* We love you kqueue, oh yes we do *sings*! + * kqueue gives us the error number directly in the EOF state! + * Unlike smelly epoll and select, where we have to getsockopt + * to get the error, this saves us time and cpu cycles. Go BSD! + */ + if (ref[ke_list[j].ident]) + ref[ke_list[j].ident]->HandleEvent(EVENT_ERROR, ke_list[j].fflags); + continue; + } + if (ke_list[j].flags & EVFILT_WRITE) + { + /* This looks wrong but its right. As above, theres no modify + * call in kqueue. See the manpage. + */ + struct kevent ke; + EV_SET(&ke, ke_list[j].ident, EVFILT_READ, EV_ADD, 0, 0, NULL); + kevent(EngineHandle, &ke, 1, 0, 0, NULL); + if (ref[ke_list[j].ident]) + ref[ke_list[j].ident]->HandleEvent(EVENT_WRITE); + } + else + { + if (ref[ke_list[j].ident]) + ref[ke_list[j].ident]->HandleEvent(EVENT_READ); + } + } + + return i; +} + +std::string KQueueEngine::GetName() +{ + return "kqueue"; +} diff --git a/src/socketengine_ports.cpp b/src/socketengine_ports.cpp index d3704f0a4..869e0a6fb 100644 --- a/src/socketengine_ports.cpp +++ b/src/socketengine_ports.cpp @@ -1 +1,129 @@ -/* +------------------------------------+
* | 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 "exitcodes.h"
#include <port.h>
#include "socketengine_ports.h"
PortsEngine::PortsEngine(InspIRCd* Instance) : SocketEngine(Instance)
{
EngineHandle = port_create();
if (EngineHandle == -1)
{
ServerInstance->Log(SPARSE,"ERROR: Could not initialize socket engine: %s", strerror(errno));
ServerInstance->Log(SPARSE,"ERROR: This is a fatal error, exiting now.");
printf("ERROR: Could not initialize socket engine: %s\n", strerror(errno));
printf("ERROR: This is a fatal error, exiting now.\n");
InspIRCd::Exit(EXIT_STATUS_SOCKETENGINE);
}
CurrentSetSize = 0;
}
PortsEngine::~PortsEngine()
{
close(EngineHandle);
}
bool PortsEngine::AddFd(EventHandler* eh)
{
int fd = eh->GetFd();
if ((fd < 0) || (fd > MAX_DESCRIPTORS))
return false;
if (GetRemainingFds() <= 1)
return false;
if (ref[fd])
return false;
ref[fd] = eh;
port_associate(EngineHandle, PORT_SOURCE_FD, fd, eh->Readable() ? POLLRDNORM : POLLWRNORM, eh);
ServerInstance->Log(DEBUG,"New file descriptor: %d", fd);
CurrentSetSize++;
return true;
}
void PortsEngine::WantWrite(EventHandler* eh)
{
port_associate(EngineHandle, PORT_SOURCE_FD, eh->GetFd(), POLLWRNORM, eh);
}
bool PortsEngine::DelFd(EventHandler* eh, bool force)
{
int fd = eh->GetFd();
if ((fd < 0) || (fd > MAX_DESCRIPTORS))
return false;
port_dissociate(EngineHandle, PORT_SOURCE_FD, fd);
CurrentSetSize--;
ref[fd] = NULL;
ServerInstance->Log(DEBUG,"Remove file descriptor: %d", fd);
return true;
}
int PortsEngine::GetMaxFds()
{
return MAX_DESCRIPTORS;
}
int PortsEngine::GetRemainingFds()
{
return MAX_DESCRIPTORS - CurrentSetSize;
}
int PortsEngine::DispatchEvents()
{
struct timespec poll_time;
poll_time.tv_sec = 1;
poll_time.tv_nsec = 0;
unsigned int nget = 1; // used to denote a retrieve request.
int i = port_getn(EngineHandle, this->events, MAX_DESCRIPTORS, &nget, &poll_time);
// first handle an error condition
if (i == -1)
return i;
for (i = 0; i < nget; i++)
{
switch (this->events[i].portev_source)
{
case PORT_SOURCE_FD:
{
int fd = this->events[i].portev_object;
if (ref[fd])
{
// reinsert port for next time around
port_associate(EngineHandle, PORT_SOURCE_FD, fd, POLLRDNORM, ref[fd]);
ref[fd]->HandleEvent((this->events[i].portev_events & POLLRDNORM) ? EVENT_READ : EVENT_WRITE);
}
}
default:
break;
}
}
return i;
}
std::string PortsEngine::GetName()
{
return "ports";
}
\ No newline at end of file +/* +------------------------------------+ + * | 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 "exitcodes.h" +#include <port.h> +#include "socketengine_ports.h" + +PortsEngine::PortsEngine(InspIRCd* Instance) : SocketEngine(Instance) +{ + EngineHandle = port_create(); + + if (EngineHandle == -1) + { + ServerInstance->Log(SPARSE,"ERROR: Could not initialize socket engine: %s", strerror(errno)); + ServerInstance->Log(SPARSE,"ERROR: This is a fatal error, exiting now."); + printf("ERROR: Could not initialize socket engine: %s\n", strerror(errno)); + printf("ERROR: This is a fatal error, exiting now.\n"); + InspIRCd::Exit(EXIT_STATUS_SOCKETENGINE); + } + CurrentSetSize = 0; +} + +PortsEngine::~PortsEngine() +{ + close(EngineHandle); +} + +bool PortsEngine::AddFd(EventHandler* eh) +{ + int fd = eh->GetFd(); + if ((fd < 0) || (fd > MAX_DESCRIPTORS)) + return false; + + if (GetRemainingFds() <= 1) + return false; + + if (ref[fd]) + return false; + + ref[fd] = eh; + port_associate(EngineHandle, PORT_SOURCE_FD, fd, eh->Readable() ? POLLRDNORM : POLLWRNORM, eh); + + ServerInstance->Log(DEBUG,"New file descriptor: %d", fd); + CurrentSetSize++; + return true; +} + +void PortsEngine::WantWrite(EventHandler* eh) +{ + port_associate(EngineHandle, PORT_SOURCE_FD, eh->GetFd(), POLLWRNORM, eh); +} + +bool PortsEngine::DelFd(EventHandler* eh, bool force) +{ + int fd = eh->GetFd(); + if ((fd < 0) || (fd > MAX_DESCRIPTORS)) + return false; + + port_dissociate(EngineHandle, PORT_SOURCE_FD, fd); + + CurrentSetSize--; + ref[fd] = NULL; + + ServerInstance->Log(DEBUG,"Remove file descriptor: %d", fd); + return true; +} + +int PortsEngine::GetMaxFds() +{ + return MAX_DESCRIPTORS; +} + +int PortsEngine::GetRemainingFds() +{ + return MAX_DESCRIPTORS - CurrentSetSize; +} + +int PortsEngine::DispatchEvents() +{ + struct timespec poll_time; + + poll_time.tv_sec = 1; + poll_time.tv_nsec = 0; + + unsigned int nget = 1; // used to denote a retrieve request. + int i = port_getn(EngineHandle, this->events, MAX_DESCRIPTORS, &nget, &poll_time); + + // first handle an error condition + if (i == -1) + return i; + + for (i = 0; i < nget; i++) + { + switch (this->events[i].portev_source) + { + case PORT_SOURCE_FD: + { + int fd = this->events[i].portev_object; + if (ref[fd]) + { + // reinsert port for next time around + port_associate(EngineHandle, PORT_SOURCE_FD, fd, POLLRDNORM, ref[fd]); + ref[fd]->HandleEvent((this->events[i].portev_events & POLLRDNORM) ? EVENT_READ : EVENT_WRITE); + } + } + default: + break; + } + } + + return i; +} + +std::string PortsEngine::GetName() +{ + return "ports"; +} + diff --git a/src/socketengine_select.cpp b/src/socketengine_select.cpp index 73e909193..ef5f2071f 100644 --- a/src/socketengine_select.cpp +++ b/src/socketengine_select.cpp @@ -1 +1,167 @@ -/* +------------------------------------+
* | 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 <sys/select.h>
#include "socketengine_select.h"
SelectEngine::SelectEngine(InspIRCd* Instance) : SocketEngine(Instance)
{
EngineHandle = 0;
CurrentSetSize = 0;
memset(writeable, 0, sizeof(writeable));
}
SelectEngine::~SelectEngine()
{
}
bool SelectEngine::AddFd(EventHandler* eh)
{
int fd = eh->GetFd();
if ((fd < 0) || (fd > MAX_DESCRIPTORS))
return false;
if (GetRemainingFds() <= 1)
return false;
fds[fd] = fd;
if (ref[fd])
return false;
ref[fd] = eh;
CurrentSetSize++;
ServerInstance->Log(DEBUG,"New file descriptor: %d", fd);
return true;
}
void SelectEngine::WantWrite(EventHandler* eh)
{
writeable[eh->GetFd()] = true;
}
bool SelectEngine::DelFd(EventHandler* eh, bool force)
{
int fd = eh->GetFd();
if ((fd < 0) || (fd > MAX_DESCRIPTORS))
return false;
std::map<int,int>::iterator t = fds.find(fd);
if (t != fds.end())
fds.erase(t);
CurrentSetSize--;
ref[fd] = NULL;
ServerInstance->Log(DEBUG,"Remove file descriptor: %d", fd);
return true;
}
int SelectEngine::GetMaxFds()
{
return FD_SETSIZE;
}
int SelectEngine::GetRemainingFds()
{
return FD_SETSIZE - CurrentSetSize;
}
int SelectEngine::DispatchEvents()
{
int result = 0;
timeval tval;
int sresult = 0;
EventHandler* ev[MAX_DESCRIPTORS];
socklen_t codesize;
int errcode;
FD_ZERO(&wfdset);
FD_ZERO(&rfdset);
FD_ZERO(&errfdset);
for (std::map<int,int>::iterator a = fds.begin(); a != fds.end(); a++)
{
if (ref[a->second]->Readable())
FD_SET (a->second, &rfdset);
else
FD_SET (a->second, &wfdset);
if (writeable[a->second])
FD_SET (a->second, &wfdset);
FD_SET (a->second, &errfdset);
}
tval.tv_sec = 1;
tval.tv_usec = 0;
sresult = select(FD_SETSIZE, &rfdset, &wfdset, &errfdset, &tval);
if (sresult > 0)
{
for (std::map<int,int>::iterator a = fds.begin(); a != fds.end(); a++)
{
if ((FD_ISSET (a->second, &rfdset)) || (FD_ISSET (a->second, &wfdset)) || FD_ISSET (a->second, &errfdset))
{
ev[result++] = ref[a->second];
}
}
}
/** An event handler may remove its own descriptor from the list, therefore it is not
* safe to directly iterate over the list and dispatch events there with STL iterators.
* Thats a shame because it makes this code slower and more resource intensive, but maybe
* the user should stop using select(), as select() smells anyway.
*/
for (int i = 0; i < result; i++)
{
if (ev[i])
{
if (FD_ISSET (ev[i]->GetFd(), &errfdset))
{
if (ev[i])
{
if (getsockopt(ev[i]->GetFd(), SOL_SOCKET, SO_ERROR, &errcode, &codesize) < 0)
errcode = errno;
ev[i]->HandleEvent(EVENT_ERROR, errcode);
}
continue;
}
if (ev[i])
{
if (writeable[ev[i]->GetFd()])
{
if (ev[i])
ev[i]->HandleEvent(EVENT_WRITE);
writeable[ev[i]->GetFd()] = false;
}
else
{
if (ev[i])
ev[i]->HandleEvent(ev[i]->Readable() ? EVENT_READ : EVENT_WRITE);
}
}
}
}
return result;
}
std::string SelectEngine::GetName()
{
return "select";
}
\ No newline at end of file +/* +------------------------------------+ + * | 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 <sys/select.h> +#include "socketengine_select.h" + + +SelectEngine::SelectEngine(InspIRCd* Instance) : SocketEngine(Instance) +{ + EngineHandle = 0; + CurrentSetSize = 0; + memset(writeable, 0, sizeof(writeable)); +} + +SelectEngine::~SelectEngine() +{ +} + +bool SelectEngine::AddFd(EventHandler* eh) +{ + int fd = eh->GetFd(); + if ((fd < 0) || (fd > MAX_DESCRIPTORS)) + return false; + + if (GetRemainingFds() <= 1) + return false; + + fds[fd] = fd; + + if (ref[fd]) + return false; + + ref[fd] = eh; + + CurrentSetSize++; + + ServerInstance->Log(DEBUG,"New file descriptor: %d", fd); + return true; +} + +void SelectEngine::WantWrite(EventHandler* eh) +{ + writeable[eh->GetFd()] = true; +} + +bool SelectEngine::DelFd(EventHandler* eh, bool force) +{ + int fd = eh->GetFd(); + + if ((fd < 0) || (fd > MAX_DESCRIPTORS)) + return false; + + std::map<int,int>::iterator t = fds.find(fd); + if (t != fds.end()) + fds.erase(t); + + CurrentSetSize--; + ref[fd] = NULL; + + ServerInstance->Log(DEBUG,"Remove file descriptor: %d", fd); + return true; +} + +int SelectEngine::GetMaxFds() +{ + return FD_SETSIZE; +} + +int SelectEngine::GetRemainingFds() +{ + return FD_SETSIZE - CurrentSetSize; +} + +int SelectEngine::DispatchEvents() +{ + int result = 0; + timeval tval; + int sresult = 0; + EventHandler* ev[MAX_DESCRIPTORS]; + socklen_t codesize; + int errcode; + + FD_ZERO(&wfdset); + FD_ZERO(&rfdset); + FD_ZERO(&errfdset); + + for (std::map<int,int>::iterator a = fds.begin(); a != fds.end(); a++) + { + if (ref[a->second]->Readable()) + FD_SET (a->second, &rfdset); + else + FD_SET (a->second, &wfdset); + if (writeable[a->second]) + FD_SET (a->second, &wfdset); + + FD_SET (a->second, &errfdset); + } + tval.tv_sec = 1; + tval.tv_usec = 0; + sresult = select(FD_SETSIZE, &rfdset, &wfdset, &errfdset, &tval); + if (sresult > 0) + { + for (std::map<int,int>::iterator a = fds.begin(); a != fds.end(); a++) + { + if ((FD_ISSET (a->second, &rfdset)) || (FD_ISSET (a->second, &wfdset)) || FD_ISSET (a->second, &errfdset)) + { + ev[result++] = ref[a->second]; + } + } + } + + /** An event handler may remove its own descriptor from the list, therefore it is not + * safe to directly iterate over the list and dispatch events there with STL iterators. + * Thats a shame because it makes this code slower and more resource intensive, but maybe + * the user should stop using select(), as select() smells anyway. + */ + for (int i = 0; i < result; i++) + { + if (ev[i]) + { + if (FD_ISSET (ev[i]->GetFd(), &errfdset)) + { + if (ev[i]) + { + if (getsockopt(ev[i]->GetFd(), SOL_SOCKET, SO_ERROR, &errcode, &codesize) < 0) + errcode = errno; + + ev[i]->HandleEvent(EVENT_ERROR, errcode); + } + continue; + } + if (ev[i]) + { + if (writeable[ev[i]->GetFd()]) + { + if (ev[i]) + ev[i]->HandleEvent(EVENT_WRITE); + writeable[ev[i]->GetFd()] = false; + + } + else + { + if (ev[i]) + ev[i]->HandleEvent(ev[i]->Readable() ? EVENT_READ : EVENT_WRITE); + } + } + } + } + + return result; +} + +std::string SelectEngine::GetName() +{ + return "select"; +} diff --git a/src/timer.cpp b/src/timer.cpp index 90cf8bd95..c04107502 100644 --- a/src/timer.cpp +++ b/src/timer.cpp @@ -1 +1,135 @@ -/* +------------------------------------+
* | 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 "timer.h"
TimerManager::TimerManager(InspIRCd* Instance) : CantDeleteHere(false), ServerInstance(Instance)
{
}
void TimerManager::TickTimers(time_t TIME)
{
this->CantDeleteHere = true;
timerlist::iterator found = Timers.find(TIME);
if (found != Timers.end())
{
timergroup* x = found->second;
/* There are pending timers to trigger.
* WARNING: Timers may delete themselves from within
* their own Tick methods! see the comment below in
* the DelTimer method.
*/
for (timergroup::iterator y = x->begin(); y != x->end(); y++)
{
InspTimer* n = *y;
n->Tick(TIME);
if (n->GetRepeat())
{
AddTimer(n, n->GetSecs());
}
else
{
DELETE(n);
}
}
Timers.erase(found);
DELETE(x);
}
this->CantDeleteHere = false;
}
void TimerManager::DelTimer(InspTimer* T)
{
if (this->CantDeleteHere)
{
/* If a developer tries to delete a timer from within its own Tick method,
* then chances are this is just going to totally fuck over the timergroup
* and timerlist iterators and cause a crash. Thanks to peavey and Bricker
* for noticing this bug.
* If we're within the tick loop when the DelTimer is called (signified
* by the var 'CantDeleteHere') then we simply return for non-repeating
* timers, and cancel the repeat on repeating timers. We can do this because
* we know that the timer tick loop will safely delete the timer for us
* anyway and therefore we avoid stack corruption.
*/
if (T->GetRepeat())
T->CancelRepeat();
else
return;
}
timerlist::iterator found = Timers.find(T->GetTimer());
if (found != Timers.end())
{
timergroup* x = found->second;
for (timergroup::iterator y = x->begin(); y != x->end(); y++)
{
InspTimer* n = *y;
if (n == T)
{
DELETE(n);
x->erase(y);
if (!x->size())
{
Timers.erase(found);
DELETE(x);
}
return;
}
}
}
}
/** Because some muppets may do odd things, and their ircd may lock up due
* to crappy 3rd party modules, or they may change their system time a bit,
* this accounts for shifts of up to 120 secs by looking behind for missed
* timers and executing them. This is only executed once every 5 secs.
* If you move your clock BACK, and your timers move further ahead as a result,
* then tough titty you'll just have to wait.
*/
void TimerManager::TickMissedTimers(time_t TIME)
{
for (time_t n = TIME-1; n > TIME-120; n--)
this->TickTimers(TIME);
}
void TimerManager::AddTimer(InspTimer* T, long secs_from_now)
{
timergroup* x = NULL;
int time_to_trigger = 0;
if (!secs_from_now)
time_to_trigger = T->GetTimer();
else
time_to_trigger = secs_from_now + ServerInstance->Time();
timerlist::iterator found = Timers.find(time_to_trigger);
if (found != Timers.end())
{
x = found->second;
}
else
{
x = new timergroup;
Timers[time_to_trigger] = x;
}
x->push_back(T);
}
\ No newline at end of file +/* +------------------------------------+ + * | 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 "timer.h" + +TimerManager::TimerManager(InspIRCd* Instance) : CantDeleteHere(false), ServerInstance(Instance) +{ +} + +void TimerManager::TickTimers(time_t TIME) +{ + this->CantDeleteHere = true; + timerlist::iterator found = Timers.find(TIME); + + if (found != Timers.end()) + { + timergroup* x = found->second; + /* There are pending timers to trigger. + * WARNING: Timers may delete themselves from within + * their own Tick methods! see the comment below in + * the DelTimer method. + */ + for (timergroup::iterator y = x->begin(); y != x->end(); y++) + { + InspTimer* n = *y; + n->Tick(TIME); + if (n->GetRepeat()) + { + AddTimer(n, n->GetSecs()); + } + else + { + DELETE(n); + } + } + + Timers.erase(found); + DELETE(x); + } + + this->CantDeleteHere = false; +} + +void TimerManager::DelTimer(InspTimer* T) +{ + if (this->CantDeleteHere) + { + /* If a developer tries to delete a timer from within its own Tick method, + * then chances are this is just going to totally fuck over the timergroup + * and timerlist iterators and cause a crash. Thanks to peavey and Bricker + * for noticing this bug. + * If we're within the tick loop when the DelTimer is called (signified + * by the var 'CantDeleteHere') then we simply return for non-repeating + * timers, and cancel the repeat on repeating timers. We can do this because + * we know that the timer tick loop will safely delete the timer for us + * anyway and therefore we avoid stack corruption. + */ + if (T->GetRepeat()) + T->CancelRepeat(); + else + return; + } + + timerlist::iterator found = Timers.find(T->GetTimer()); + + if (found != Timers.end()) + { + timergroup* x = found->second; + for (timergroup::iterator y = x->begin(); y != x->end(); y++) + { + InspTimer* n = *y; + if (n == T) + { + DELETE(n); + x->erase(y); + if (!x->size()) + { + Timers.erase(found); + DELETE(x); + } + return; + } + } + } +} + +/** Because some muppets may do odd things, and their ircd may lock up due + * to crappy 3rd party modules, or they may change their system time a bit, + * this accounts for shifts of up to 120 secs by looking behind for missed + * timers and executing them. This is only executed once every 5 secs. + * If you move your clock BACK, and your timers move further ahead as a result, + * then tough titty you'll just have to wait. + */ +void TimerManager::TickMissedTimers(time_t TIME) +{ + for (time_t n = TIME-1; n > TIME-120; n--) + this->TickTimers(TIME); +} + +void TimerManager::AddTimer(InspTimer* T, long secs_from_now) +{ + timergroup* x = NULL; + + int time_to_trigger = 0; + if (!secs_from_now) + time_to_trigger = T->GetTimer(); + else + time_to_trigger = secs_from_now + ServerInstance->Time(); + + timerlist::iterator found = Timers.find(time_to_trigger); + + if (found != Timers.end()) + { + x = found->second; + } + else + { + x = new timergroup; + Timers[time_to_trigger] = x; + } + + x->push_back(T); +} + diff --git a/src/userprocess.cpp b/src/userprocess.cpp index c2b2f0b83..b27844fb6 100644 --- a/src/userprocess.cpp +++ b/src/userprocess.cpp @@ -1 +1,305 @@ -/* +------------------------------------+
* | 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 "users.h"
#include "modules.h"
#include "wildcard.h"
#include "xline.h"
#include "socketengine.h"
#include "command_parse.h"
void InspIRCd::FloodQuitUser(userrec* current)
{
this->Log(DEFAULT,"Excess flood from: %s@%s", current->ident, current->host);
this->SNO->WriteToSnoMask('f',"Excess flood from: %s%s%s@%s",
current->registered == REG_ALL ? current->nick : "",
current->registered == REG_ALL ? "!" : "", current->ident, current->host);
current->SetWriteError("Excess flood");
if (current->registered != REG_ALL)
{
XLines->add_zline(120,this->Config->ServerName,"Flood from unregistered connection",current->GetIPString());
XLines->apply_lines(APPLY_ZLINES);
}
}
void InspIRCd::ProcessUser(userrec* cu)
{
int result = EAGAIN;
if (cu->GetFd() == FD_MAGIC_NUMBER)
return;
if (this->Config->GetIOHook(cu->GetPort()))
{
int result2 = 0;
int MOD_RESULT = 0;
try
{
MOD_RESULT = this->Config->GetIOHook(cu->GetPort())->OnRawSocketRead(cu->GetFd(),ReadBuffer,sizeof(ReadBuffer),result2);
}
catch (CoreException& modexcept)
{
this->Log(DEBUG, "%s threw an exception: %s", modexcept.GetSource(), modexcept.GetReason());
}
if (MOD_RESULT < 0)
{
result = -EAGAIN;
}
else
{
result = result2;
}
}
else
{
result = cu->ReadData(ReadBuffer, sizeof(ReadBuffer));
}
if ((result) && (result != -EAGAIN))
{
userrec *current;
int currfd;
int floodlines = 0;
this->stats->statsRecv += result;
/*
* perform a check on the raw buffer as an array (not a string!) to remove
* character 0 which is illegal in the RFC - replace them with spaces.
* XXX - no garauntee there's not \0's in the middle of the data,
* and no reason for it to be terminated either. -- Om
*/
for (int checker = 0; checker < result; checker++)
{
if (ReadBuffer[checker] == 0)
ReadBuffer[checker] = ' ';
}
if (result > 0)
ReadBuffer[result] = '\0';
current = cu;
currfd = current->GetFd();
// add the data to the users buffer
if (result > 0)
{
if (!current->AddBuffer(ReadBuffer))
{
// AddBuffer returned false, theres too much data in the user's buffer and theyre up to no good.
if (current->registered == REG_ALL)
{
// Make sure they arn't flooding long lines.
if (TIME > current->reset_due)
{
current->reset_due = TIME + current->threshold;
current->lines_in = 0;
}
current->lines_in++;
if (current->flood && current->lines_in > current->flood)
FloodQuitUser(current);
else
{
current->WriteServ("NOTICE %s :Your previous line was too long and was not delivered (Over %d chars) Please shorten it.", current->nick, MAXBUF-2);
current->recvq.clear();
}
}
else
FloodQuitUser(current);
return;
}
// while there are complete lines to process...
while (current->BufferIsReady())
{
if (TIME > current->reset_due)
{
current->reset_due = TIME + current->threshold;
current->lines_in = 0;
}
if (++current->lines_in > current->flood && current->flood)
{
FloodQuitUser(current);
return;
}
if ((++floodlines > current->flood) && (current->flood != 0))
{
FloodQuitUser(current);
return;
}
// use GetBuffer to copy single lines into the sanitized string
std::string single_line = current->GetBuffer();
current->bytes_in += single_line.length();
current->cmds_in++;
if (single_line.length() > MAXBUF - 2) /* MAXBUF is 514 to allow for neccessary line terminators */
single_line.resize(MAXBUF - 2); /* So to trim to 512 here, we use MAXBUF - 2 */
EventHandler* old_comp = this->SE->GetRef(currfd);
this->Parser->ProcessBuffer(single_line,current);
/*
* look for the user's record in case it's changed... if theyve quit,
* we cant do anything more with their buffer, so bail.
* there used to be an ugly, slow loop here. Now we have a reference
* table, life is much easier (and FASTER)
*/
EventHandler* new_comp = this->SE->GetRef(currfd);
if (new_comp != old_comp)
return;
}
return;
}
if ((result == -1) && (errno != EAGAIN) && (errno != EINTR))
{
cu->SetWriteError(strerror(errno));
return;
}
}
// result EAGAIN means nothing read
else if ((result == EAGAIN) || (result == -EAGAIN))
{
/* do nothing */
}
else if (result == 0)
{
cu->SetWriteError("Connection closed");
return;
}
}
/**
* This function is called once a second from the mainloop.
* It is intended to do background checking on all the user structs, e.g.
* stuff like ping checks, registration timeouts, etc.
*/
void InspIRCd::DoBackgroundUserStuff(time_t TIME)
{
/* Is it time yet? */
if (TIME < next_call)
return;
else
{
/* Time we actually need to call this again */
const time_t DUMMY_VALUE = 32768;
next_call = TIME + DUMMY_VALUE;
/* XXX: IT IS NOT SAFE TO USE AN ITERATOR HERE. DON'T EVEN THINK ABOUT IT. */
for (unsigned long count2 = 0; count2 != this->local_users.size(); count2++)
{
if (count2 >= this->local_users.size())
break;
userrec* curr = this->local_users[count2];
if (curr)
{
/*
* registration timeout -- didnt send USER/NICK/HOST
* in the time specified in their connection class.
*/
if ((TIME > curr->timeout) && (curr->registered != REG_ALL))
{
curr->muted = true;
GlobalCulls.AddItem(curr,"Registration timeout");
continue;
}
else
{
if ((curr->registered != REG_ALL) && (next_call > (time_t)curr->timeout))
next_call = curr->timeout;
}
/*
* user has signed on with USER/NICK/PASS, and dns has completed, all the modules
* say this user is ok to proceed, fully connect them.
*/
bool ready = AllModulesReportReady(curr);
if ((TIME > curr->signon) && (curr->registered == REG_NICKUSER) && (ready))
{
if (!curr->dns_done)
{
curr->WriteServ("NOTICE Auth :*** Could not resolve your hostname: Request timed out; using your IP address (%s) instead.", curr->GetIPString());
curr->dns_done = true;
}
this->stats->statsDnsBad++;
curr->FullConnect();
continue;
}
else
{
if ((curr->registered == REG_NICKUSER) && (ready) && (next_call > curr->signon))
next_call = curr->signon;
}
if ((curr->dns_done) && (curr->registered == REG_NICKUSER) && (ready))
{
curr->FullConnect();
continue;
}
else
{
if ((curr->registered == REG_NICKUSER) && (ready) && (next_call > curr->signon + this->Config->dns_timeout))
next_call = curr->signon + this->Config->dns_timeout;
}
// It's time to PING this user. Send them a ping.
if ((TIME > curr->nping) && (curr->registered == REG_ALL))
{
// This user didn't answer the last ping, remove them
if (!curr->lastping)
{
/* Everybody loves boobies. */
time_t time = this->Time(false) - (curr->nping - curr->pingmax);
char message[MAXBUF];
snprintf(message, MAXBUF, "Ping timeout: %ld second%s", time, time > 1 ? "s" : "");
curr->muted = true;
GlobalCulls.AddItem(curr, message);
curr->lastping = 1;
curr->nping = TIME+curr->pingmax;
continue;
}
curr->Write("PING :%s",this->Config->ServerName);
curr->lastping = 0;
curr->nping = TIME+curr->pingmax;
}
else
{
if ((curr->registered == REG_ALL) && (next_call > curr->nping))
next_call = curr->nping;
}
}
}
/* If theres nothing to do, trigger in the next second, something might come up */
time_t delta = next_call - TIME;
if (delta == DUMMY_VALUE)
{
next_call = TIME + 1;
delta = 1;
}
}
}
\ No newline at end of file +/* +------------------------------------+ + * | 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 "users.h" +#include "modules.h" +#include "wildcard.h" +#include "xline.h" +#include "socketengine.h" +#include "command_parse.h" + +void InspIRCd::FloodQuitUser(userrec* current) +{ + this->Log(DEFAULT,"Excess flood from: %s@%s", current->ident, current->host); + this->SNO->WriteToSnoMask('f',"Excess flood from: %s%s%s@%s", + current->registered == REG_ALL ? current->nick : "", + current->registered == REG_ALL ? "!" : "", current->ident, current->host); + current->SetWriteError("Excess flood"); + if (current->registered != REG_ALL) + { + XLines->add_zline(120,this->Config->ServerName,"Flood from unregistered connection",current->GetIPString()); + XLines->apply_lines(APPLY_ZLINES); + } +} + +void InspIRCd::ProcessUser(userrec* cu) +{ + int result = EAGAIN; + + if (cu->GetFd() == FD_MAGIC_NUMBER) + return; + + if (this->Config->GetIOHook(cu->GetPort())) + { + int result2 = 0; + int MOD_RESULT = 0; + + try + { + MOD_RESULT = this->Config->GetIOHook(cu->GetPort())->OnRawSocketRead(cu->GetFd(),ReadBuffer,sizeof(ReadBuffer),result2); + } + catch (CoreException& modexcept) + { + this->Log(DEBUG, "%s threw an exception: %s", modexcept.GetSource(), modexcept.GetReason()); + } + + if (MOD_RESULT < 0) + { + result = -EAGAIN; + } + else + { + result = result2; + } + } + else + { + result = cu->ReadData(ReadBuffer, sizeof(ReadBuffer)); + } + + if ((result) && (result != -EAGAIN)) + { + userrec *current; + int currfd; + int floodlines = 0; + + this->stats->statsRecv += result; + /* + * perform a check on the raw buffer as an array (not a string!) to remove + * character 0 which is illegal in the RFC - replace them with spaces. + * XXX - no garauntee there's not \0's in the middle of the data, + * and no reason for it to be terminated either. -- Om + */ + + for (int checker = 0; checker < result; checker++) + { + if (ReadBuffer[checker] == 0) + ReadBuffer[checker] = ' '; + } + + if (result > 0) + ReadBuffer[result] = '\0'; + + current = cu; + currfd = current->GetFd(); + + // add the data to the users buffer + if (result > 0) + { + if (!current->AddBuffer(ReadBuffer)) + { + // AddBuffer returned false, theres too much data in the user's buffer and theyre up to no good. + if (current->registered == REG_ALL) + { + // Make sure they arn't flooding long lines. + if (TIME > current->reset_due) + { + current->reset_due = TIME + current->threshold; + current->lines_in = 0; + } + + current->lines_in++; + + if (current->flood && current->lines_in > current->flood) + FloodQuitUser(current); + else + { + current->WriteServ("NOTICE %s :Your previous line was too long and was not delivered (Over %d chars) Please shorten it.", current->nick, MAXBUF-2); + current->recvq.clear(); + } + } + else + FloodQuitUser(current); + + return; + } + + // while there are complete lines to process... + while (current->BufferIsReady()) + { + if (TIME > current->reset_due) + { + current->reset_due = TIME + current->threshold; + current->lines_in = 0; + } + + if (++current->lines_in > current->flood && current->flood) + { + FloodQuitUser(current); + return; + } + + if ((++floodlines > current->flood) && (current->flood != 0)) + { + FloodQuitUser(current); + return; + } + + // use GetBuffer to copy single lines into the sanitized string + std::string single_line = current->GetBuffer(); + current->bytes_in += single_line.length(); + current->cmds_in++; + if (single_line.length() > MAXBUF - 2) /* MAXBUF is 514 to allow for neccessary line terminators */ + single_line.resize(MAXBUF - 2); /* So to trim to 512 here, we use MAXBUF - 2 */ + + EventHandler* old_comp = this->SE->GetRef(currfd); + + this->Parser->ProcessBuffer(single_line,current); + /* + * look for the user's record in case it's changed... if theyve quit, + * we cant do anything more with their buffer, so bail. + * there used to be an ugly, slow loop here. Now we have a reference + * table, life is much easier (and FASTER) + */ + EventHandler* new_comp = this->SE->GetRef(currfd); + + if (new_comp != old_comp) + return; + } + + return; + } + + if ((result == -1) && (errno != EAGAIN) && (errno != EINTR)) + { + cu->SetWriteError(strerror(errno)); + return; + } + } + + // result EAGAIN means nothing read + else if ((result == EAGAIN) || (result == -EAGAIN)) + { + /* do nothing */ + } + else if (result == 0) + { + cu->SetWriteError("Connection closed"); + return; + } +} + +/** + * This function is called once a second from the mainloop. + * It is intended to do background checking on all the user structs, e.g. + * stuff like ping checks, registration timeouts, etc. + */ +void InspIRCd::DoBackgroundUserStuff(time_t TIME) +{ + /* Is it time yet? */ + if (TIME < next_call) + return; + else + { + /* Time we actually need to call this again */ + const time_t DUMMY_VALUE = 32768; + next_call = TIME + DUMMY_VALUE; + + /* XXX: IT IS NOT SAFE TO USE AN ITERATOR HERE. DON'T EVEN THINK ABOUT IT. */ + for (unsigned long count2 = 0; count2 != this->local_users.size(); count2++) + { + if (count2 >= this->local_users.size()) + break; + + userrec* curr = this->local_users[count2]; + + if (curr) + { + /* + * registration timeout -- didnt send USER/NICK/HOST + * in the time specified in their connection class. + */ + if ((TIME > curr->timeout) && (curr->registered != REG_ALL)) + { + curr->muted = true; + GlobalCulls.AddItem(curr,"Registration timeout"); + continue; + } + else + { + if ((curr->registered != REG_ALL) && (next_call > (time_t)curr->timeout)) + next_call = curr->timeout; + } + + /* + * user has signed on with USER/NICK/PASS, and dns has completed, all the modules + * say this user is ok to proceed, fully connect them. + */ + bool ready = AllModulesReportReady(curr); + if ((TIME > curr->signon) && (curr->registered == REG_NICKUSER) && (ready)) + { + if (!curr->dns_done) + { + curr->WriteServ("NOTICE Auth :*** Could not resolve your hostname: Request timed out; using your IP address (%s) instead.", curr->GetIPString()); + curr->dns_done = true; + } + this->stats->statsDnsBad++; + curr->FullConnect(); + continue; + } + else + { + if ((curr->registered == REG_NICKUSER) && (ready) && (next_call > curr->signon)) + next_call = curr->signon; + } + + if ((curr->dns_done) && (curr->registered == REG_NICKUSER) && (ready)) + { + curr->FullConnect(); + continue; + } + else + { + if ((curr->registered == REG_NICKUSER) && (ready) && (next_call > curr->signon + this->Config->dns_timeout)) + next_call = curr->signon + this->Config->dns_timeout; + } + + // It's time to PING this user. Send them a ping. + if ((TIME > curr->nping) && (curr->registered == REG_ALL)) + { + // This user didn't answer the last ping, remove them + if (!curr->lastping) + { + /* Everybody loves boobies. */ + time_t time = this->Time(false) - (curr->nping - curr->pingmax); + char message[MAXBUF]; + snprintf(message, MAXBUF, "Ping timeout: %ld second%s", time, time > 1 ? "s" : ""); + curr->muted = true; + GlobalCulls.AddItem(curr, message); + curr->lastping = 1; + curr->nping = TIME+curr->pingmax; + continue; + } + curr->Write("PING :%s",this->Config->ServerName); + curr->lastping = 0; + curr->nping = TIME+curr->pingmax; + } + else + { + if ((curr->registered == REG_ALL) && (next_call > curr->nping)) + next_call = curr->nping; + } + } + } + + /* If theres nothing to do, trigger in the next second, something might come up */ + time_t delta = next_call - TIME; + if (delta == DUMMY_VALUE) + { + next_call = TIME + 1; + delta = 1; + } + } +} diff --git a/src/users.cpp b/src/users.cpp index 250586f1d..7da7d6b09 100644 --- a/src/users.cpp +++ b/src/users.cpp @@ -1 +1,2007 @@ -/* +------------------------------------+
* | 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;
}
\ No newline at end of file +/* +------------------------------------+ + * | 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; +} + diff --git a/src/version.sh b/src/version.sh index bb0c9124d..e9733c2a4 100755 --- a/src/version.sh +++ b/src/version.sh @@ -1 +1,2 @@ -#!sh
echo "InspIRCd-1.1.9+IsleOfMull"
\ No newline at end of file +#!sh +echo "InspIRCd-1.1.9+IsleOfMull" diff --git a/src/wildcard.cpp b/src/wildcard.cpp index eeb6190f2..8587f1714 100644 --- a/src/wildcard.cpp +++ b/src/wildcard.cpp @@ -1 +1,148 @@ -/* +------------------------------------+
* | 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 <string>
#include "hashcomp.h"
#include "inspstring.h"
using irc::sockets::MatchCIDR;
// Wed 27 Apr 2005 - Brain
// I've taken our our old wildcard routine -
// although comprehensive, it was topheavy and very
// slow, and ate masses of cpu when doing lots of
// comparisons. This is the 'de-facto' routine used
// by many, nobody really knows who wrote it first
// or what license its under, i've seen examples of it
// (unattributed to any author) all over the 'net.
// For now, we'll just consider this public domain.
CoreExport bool csmatch(const char *str, const char *mask)
{
unsigned char *cp = NULL, *mp = NULL;
unsigned char* string = (unsigned char*)str;
unsigned char* wild = (unsigned char*)mask;
while ((*string) && (*wild != '*'))
{
if ((*wild != *string) && (*wild != '?'))
{
return 0;
}
wild++;
string++;
}
while (*string)
{
if (*wild == '*')
{
if (!*++wild)
{
return 1;
}
mp = wild;
cp = string+1;
}
else
if ((*wild == *string) || (*wild == '?'))
{
wild++;
string++;
}
else
{
wild = mp;
string = cp++;
}
}
while (*wild == '*')
{
wild++;
}
return !*wild;
}
CoreExport bool match(const char *str, const char *mask)
{
unsigned char *cp = NULL, *mp = NULL;
unsigned char* string = (unsigned char*)str;
unsigned char* wild = (unsigned char*)mask;
while ((*string) && (*wild != '*'))
{
if ((lowermap[*wild] != lowermap[*string]) && (*wild != '?'))
{
return 0;
}
wild++;
string++;
}
while (*string)
{
if (*wild == '*')
{
if (!*++wild)
{
return 1;
}
mp = wild;
cp = string+1;
}
else
if ((lowermap[*wild] == lowermap[*string]) || (*wild == '?'))
{
wild++;
string++;
}
else
{
wild = mp;
string = cp++;
}
}
while (*wild == '*')
{
wild++;
}
return !*wild;
}
/* Overloaded function that has the option of using cidr */
CoreExport bool match(const char *str, const char *mask, bool use_cidr_match)
{
if (use_cidr_match && MatchCIDR(str, mask, true))
return true;
return match(str, mask);
}
CoreExport bool match(bool case_sensitive, const char *str, const char *mask, bool use_cidr_match)
{
if (use_cidr_match && MatchCIDR(str, mask, true))
return true;
return csmatch(str, mask);
}
CoreExport bool match(bool case_sensitive, const char *str, const char *mask)
{
return case_sensitive ? csmatch(str, mask) : match(str, mask);
}
\ No newline at end of file +/* +------------------------------------+ + * | 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 <string> +#include "hashcomp.h" +#include "inspstring.h" + +using irc::sockets::MatchCIDR; + +// Wed 27 Apr 2005 - Brain +// I've taken our our old wildcard routine - +// although comprehensive, it was topheavy and very +// slow, and ate masses of cpu when doing lots of +// comparisons. This is the 'de-facto' routine used +// by many, nobody really knows who wrote it first +// or what license its under, i've seen examples of it +// (unattributed to any author) all over the 'net. +// For now, we'll just consider this public domain. + +CoreExport bool csmatch(const char *str, const char *mask) +{ + unsigned char *cp = NULL, *mp = NULL; + unsigned char* string = (unsigned char*)str; + unsigned char* wild = (unsigned char*)mask; + + while ((*string) && (*wild != '*')) + { + if ((*wild != *string) && (*wild != '?')) + { + return 0; + } + wild++; + string++; + } + + while (*string) + { + if (*wild == '*') + { + if (!*++wild) + { + return 1; + } + mp = wild; + cp = string+1; + } + else + if ((*wild == *string) || (*wild == '?')) + { + wild++; + string++; + } + else + { + wild = mp; + string = cp++; + } + + } + + while (*wild == '*') + { + wild++; + } + + return !*wild; +} + +CoreExport bool match(const char *str, const char *mask) +{ + unsigned char *cp = NULL, *mp = NULL; + unsigned char* string = (unsigned char*)str; + unsigned char* wild = (unsigned char*)mask; + + while ((*string) && (*wild != '*')) + { + if ((lowermap[*wild] != lowermap[*string]) && (*wild != '?')) + { + return 0; + } + wild++; + string++; + } + + while (*string) + { + if (*wild == '*') + { + if (!*++wild) + { + return 1; + } + mp = wild; + cp = string+1; + } + else + if ((lowermap[*wild] == lowermap[*string]) || (*wild == '?')) + { + wild++; + string++; + } + else + { + wild = mp; + string = cp++; + } + + } + + while (*wild == '*') + { + wild++; + } + + return !*wild; +} + +/* Overloaded function that has the option of using cidr */ +CoreExport bool match(const char *str, const char *mask, bool use_cidr_match) +{ + if (use_cidr_match && MatchCIDR(str, mask, true)) + return true; + return match(str, mask); +} + +CoreExport bool match(bool case_sensitive, const char *str, const char *mask, bool use_cidr_match) +{ + if (use_cidr_match && MatchCIDR(str, mask, true)) + return true; + return csmatch(str, mask); +} + +CoreExport bool match(bool case_sensitive, const char *str, const char *mask) +{ + return case_sensitive ? csmatch(str, mask) : match(str, mask); +} + diff --git a/src/xline.cpp b/src/xline.cpp index 87b1d3e5e..def9db42b 100644 --- a/src/xline.cpp +++ b/src/xline.cpp @@ -1 +1,897 @@ -/* +------------------------------------+
* | 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 "users.h"
#include "modules.h"
#include "wildcard.h"
#include "xline.h"
/* Version two, now with optimized expiry!
*
* Because the old way was horrendously slow, the new way of expiring xlines is very
* very efficient. I have improved the efficiency of the algorithm in two ways:
*
* (1) There are now two lists of items for each linetype. One list holds temporary
* items, and the other list holds permanent items (ones which will expire).
* Items which are on the permanent list are NEVER checked at all by the
* expire_lines() function.
* (2) The temporary xline lists are always kept in strict numerical order, keyed by
* current time + duration. This means that the line which is due to expire the
* soonest is always pointed at by vector::begin(), so a simple while loop can
* very efficiently, very quickly and above all SAFELY pick off the first few
* items in the vector which need zapping.
*
* -- Brain
*/
bool InitXLine(ServerConfig* conf, const char* tag)
{
return true;
}
bool DoneZLine(ServerConfig* conf, const char* tag)
{
conf->GetInstance()->XLines->apply_lines(APPLY_ZLINES|APPLY_PERM_ONLY);
return true;
}
bool DoneQLine(ServerConfig* conf, const char* tag)
{
conf->GetInstance()->XLines->apply_lines(APPLY_QLINES|APPLY_PERM_ONLY);
return true;
}
bool DoneKLine(ServerConfig* conf, const char* tag)
{
conf->GetInstance()->XLines->apply_lines(APPLY_KLINES|APPLY_PERM_ONLY);
return true;
}
bool DoneELine(ServerConfig* conf, const char* tag)
{
/* Yes, this is supposed to do nothing, we dont 'apply' these */
return true;
}
bool DoZLine(ServerConfig* conf, const char* tag, char** entries, ValueList &values, int* types)
{
const char* reason = values[0].GetString();
const char* ipmask = values[1].GetString();
conf->GetInstance()->XLines->add_zline(0,"<Config>",reason,ipmask);
return true;
}
bool DoQLine(ServerConfig* conf, const char* tag, char** entries, ValueList &values, int* types)
{
const char* reason = values[0].GetString();
const char* nick = values[1].GetString();
conf->GetInstance()->XLines->add_qline(0,"<Config>",reason,nick);
return true;
}
bool DoKLine(ServerConfig* conf, const char* tag, char** entries, ValueList &values, int* types)
{
const char* reason = values[0].GetString();
const char* host = values[1].GetString();
conf->GetInstance()->XLines->add_kline(0,"<Config>",reason,host);
return true;
}
bool DoELine(ServerConfig* conf, const char* tag, char** entries, ValueList &values, int* types)
{
const char* reason = values[0].GetString();
const char* host = values[1].GetString();
conf->GetInstance()->XLines->add_eline(0,"<Config>",reason,host);
return true;
}
IdentHostPair XLineManager::IdentSplit(const std::string &ident_and_host)
{
IdentHostPair n = std::make_pair<std::string,std::string>("*","*");
std::string::size_type x = ident_and_host.find('@');
if (x != std::string::npos)
{
n.second = ident_and_host.substr(x + 1,ident_and_host.length());
n.first = ident_and_host.substr(0, x);
if (!n.first.length())
n.first.assign("*");
if (!n.second.length())
n.second.assign("*");
}
else
{
n.second = ident_and_host;
}
return n;
}
// adds a g:line
bool XLineManager::add_gline(long duration, const char* source,const char* reason,const char* hostmask)
{
IdentHostPair ih = IdentSplit(hostmask);
if (del_gline(hostmask, true))
return false;
GLine* item = new GLine(ServerInstance->Time(), duration, source, reason, ih.first.c_str(), ih.second.c_str());
if (duration)
{
glines.push_back(item);
sort(glines.begin(), glines.end(),XLineManager::GSortComparison);
}
else
{
pglines.push_back(item);
}
return true;
}
// adds an e:line (exception to bans)
bool XLineManager::add_eline(long duration, const char* source, const char* reason, const char* hostmask)
{
IdentHostPair ih = IdentSplit(hostmask);
if (del_eline(hostmask, true))
return false;
ELine* item = new ELine(ServerInstance->Time(), duration, source, reason, ih.first.c_str(), ih.second.c_str());
if (duration)
{
elines.push_back(item);
sort(elines.begin(), elines.end(),XLineManager::ESortComparison);
}
else
{
pelines.push_back(item);
}
return true;
}
// adds a q:line
bool XLineManager::add_qline(long duration, const char* source, const char* reason, const char* nickname)
{
if (del_qline(nickname, true))
return false;
QLine* item = new QLine(ServerInstance->Time(), duration, source, reason, nickname);
if (duration)
{
qlines.push_back(item);
sort(qlines.begin(), qlines.end(),XLineManager::QSortComparison);
}
else
{
pqlines.push_back(item);
}
return true;
}
// adds a z:line
bool XLineManager::add_zline(long duration, const char* source, const char* reason, const char* ipaddr)
{
if (strchr(ipaddr,'@'))
{
while (*ipaddr != '@')
ipaddr++;
ipaddr++;
}
if (del_zline(ipaddr, true))
return false;
ZLine* item = new ZLine(ServerInstance->Time(), duration, source, reason, ipaddr);
if (duration)
{
zlines.push_back(item);
sort(zlines.begin(), zlines.end(),XLineManager::ZSortComparison);
}
else
{
pzlines.push_back(item);
}
return true;
}
// adds a k:line
bool XLineManager::add_kline(long duration, const char* source, const char* reason, const char* hostmask)
{
IdentHostPair ih = IdentSplit(hostmask);
if (del_kline(hostmask, true))
return false;
KLine* item = new KLine(ServerInstance->Time(), duration, source, reason, ih.first.c_str(), ih.second.c_str());
if (duration)
{
klines.push_back(item);
sort(klines.begin(), klines.end(),XLineManager::KSortComparison);
}
else
{
pklines.push_back(item);
}
return true;
}
// deletes a g:line, returns true if the line existed and was removed
bool XLineManager::del_gline(const char* hostmask, bool simulate)
{
IdentHostPair ih = IdentSplit(hostmask);
for (std::vector<GLine*>::iterator i = glines.begin(); i != glines.end(); i++)
{
if (!strcasecmp(ih.first.c_str(),(*i)->identmask) && !strcasecmp(ih.second.c_str(),(*i)->hostmask))
{
if (!simulate)
{
delete *i;
glines.erase(i);
}
return true;
}
}
for (std::vector<GLine*>::iterator i = pglines.begin(); i != pglines.end(); i++)
{
if (!strcasecmp(ih.first.c_str(),(*i)->identmask) && !strcasecmp(ih.second.c_str(),(*i)->hostmask))
{
if (!simulate)
{
delete *i;
pglines.erase(i);
}
return true;
}
}
return false;
}
// deletes a e:line, returns true if the line existed and was removed
bool XLineManager::del_eline(const char* hostmask, bool simulate)
{
IdentHostPair ih = IdentSplit(hostmask);
for (std::vector<ELine*>::iterator i = elines.begin(); i != elines.end(); i++)
{
if (!strcasecmp(ih.first.c_str(),(*i)->identmask) && !strcasecmp(ih.second.c_str(),(*i)->hostmask))
{
if (!simulate)
{
delete *i;
elines.erase(i);
}
return true;
}
}
for (std::vector<ELine*>::iterator i = pelines.begin(); i != pelines.end(); i++)
{
if (!strcasecmp(ih.first.c_str(),(*i)->identmask) && !strcasecmp(ih.second.c_str(),(*i)->hostmask))
{
if (!simulate)
{
delete *i;
pelines.erase(i);
}
return true;
}
}
return false;
}
// deletes a q:line, returns true if the line existed and was removed
bool XLineManager::del_qline(const char* nickname, bool simulate)
{
for (std::vector<QLine*>::iterator i = qlines.begin(); i != qlines.end(); i++)
{
if (!strcasecmp(nickname,(*i)->nick))
{
if (!simulate)
{
delete *i;
qlines.erase(i);
}
return true;
}
}
for (std::vector<QLine*>::iterator i = pqlines.begin(); i != pqlines.end(); i++)
{
if (!strcasecmp(nickname,(*i)->nick))
{
if (!simulate)
{
delete *i;
pqlines.erase(i);
}
return true;
}
}
return false;
}
// deletes a z:line, returns true if the line existed and was removed
bool XLineManager::del_zline(const char* ipaddr, bool simulate)
{
for (std::vector<ZLine*>::iterator i = zlines.begin(); i != zlines.end(); i++)
{
if (!strcasecmp(ipaddr,(*i)->ipaddr))
{
if (!simulate)
{
delete *i;
zlines.erase(i);
}
return true;
}
}
for (std::vector<ZLine*>::iterator i = pzlines.begin(); i != pzlines.end(); i++)
{
if (!strcasecmp(ipaddr,(*i)->ipaddr))
{
if (!simulate)
{
delete *i;
pzlines.erase(i);
}
return true;
}
}
return false;
}
// deletes a k:line, returns true if the line existed and was removed
bool XLineManager::del_kline(const char* hostmask, bool simulate)
{
IdentHostPair ih = IdentSplit(hostmask);
for (std::vector<KLine*>::iterator i = klines.begin(); i != klines.end(); i++)
{
if (!strcasecmp(ih.first.c_str(),(*i)->identmask) && !strcasecmp(ih.second.c_str(),(*i)->hostmask))
{
if (!simulate)
{
delete *i;
klines.erase(i);
}
return true;
}
}
for (std::vector<KLine*>::iterator i = pklines.begin(); i != pklines.end(); i++)
{
if (!strcasecmp(ih.first.c_str(),(*i)->identmask) && !strcasecmp(ih.second.c_str(),(*i)->hostmask))
{
if (!simulate)
{
delete *i;
pklines.erase(i);
}
return true;
}
}
return false;
}
// returns a pointer to the reason if a nickname matches a qline, NULL if it didnt match
QLine* XLineManager::matches_qline(const char* nick, bool permonly)
{
if ((qlines.empty()) && (pqlines.empty()))
return NULL;
if (!permonly)
{
for (std::vector<QLine*>::iterator i = qlines.begin(); i != qlines.end(); i++)
if (match(nick,(*i)->nick))
return (*i);
}
for (std::vector<QLine*>::iterator i = pqlines.begin(); i != pqlines.end(); i++)
if (match(nick,(*i)->nick))
return (*i);
return NULL;
}
// returns a pointer to the reason if a host matches a gline, NULL if it didnt match
GLine* XLineManager::matches_gline(userrec* user, bool permonly)
{
if ((glines.empty()) && (pglines.empty()))
return NULL;
if (!permonly)
{
for (std::vector<GLine*>::iterator i = glines.begin(); i != glines.end(); i++)
{
if ((match(user->ident,(*i)->identmask)))
{
if ((match(user->host,(*i)->hostmask, true)) || (match(user->GetIPString(),(*i)->hostmask, true)))
{
return (*i);
}
}
}
}
for (std::vector<GLine*>::iterator i = pglines.begin(); i != pglines.end(); i++)
{
if ((match(user->ident,(*i)->identmask)))
{
if ((match(user->host,(*i)->hostmask, true)) || (match(user->GetIPString(),(*i)->hostmask, true)))
{
return (*i);
}
}
}
return NULL;
}
ELine* XLineManager::matches_exception(userrec* user, bool permonly)
{
if ((elines.empty()) && (pelines.empty()))
return NULL;
char host2[MAXBUF];
snprintf(host2,MAXBUF,"*@%s",user->host);
if (!permonly)
{
for (std::vector<ELine*>::iterator i = elines.begin(); i != elines.end(); i++)
{
if ((match(user->ident,(*i)->identmask)))
{
if ((match(user->host,(*i)->hostmask, true)) || (match(user->GetIPString(),(*i)->hostmask, true)))
{
return (*i);
}
}
}
}
for (std::vector<ELine*>::iterator i = pelines.begin(); i != pelines.end(); i++)
{
if ((match(user->ident,(*i)->identmask)))
{
if ((match(user->host,(*i)->hostmask, true)) || (match(user->GetIPString(),(*i)->hostmask, true)))
{
return (*i);
}
}
}
return NULL;
}
void XLineManager::gline_set_creation_time(const char* host, time_t create_time)
{
for (std::vector<GLine*>::iterator i = glines.begin(); i != glines.end(); i++)
{
if (!strcasecmp(host,(*i)->hostmask))
{
(*i)->set_time = create_time;
(*i)->expiry = create_time + (*i)->duration;
return;
}
}
for (std::vector<GLine*>::iterator i = pglines.begin(); i != pglines.end(); i++)
{
if (!strcasecmp(host,(*i)->hostmask))
{
(*i)->set_time = create_time;
return;
}
}
return ;
}
void XLineManager::eline_set_creation_time(const char* host, time_t create_time)
{
for (std::vector<ELine*>::iterator i = elines.begin(); i != elines.end(); i++)
{
if (!strcasecmp(host,(*i)->hostmask))
{
(*i)->set_time = create_time;
(*i)->expiry = create_time + (*i)->duration;
return;
}
}
for (std::vector<ELine*>::iterator i = pelines.begin(); i != pelines.end(); i++)
{
if (!strcasecmp(host,(*i)->hostmask))
{
(*i)->set_time = create_time;
return;
}
}
return;
}
void XLineManager::qline_set_creation_time(const char* nick, time_t create_time)
{
for (std::vector<QLine*>::iterator i = qlines.begin(); i != qlines.end(); i++)
{
if (!strcasecmp(nick,(*i)->nick))
{
(*i)->set_time = create_time;
(*i)->expiry = create_time + (*i)->duration;
return;
}
}
for (std::vector<QLine*>::iterator i = pqlines.begin(); i != pqlines.end(); i++)
{
if (!strcasecmp(nick,(*i)->nick))
{
(*i)->set_time = create_time;
return;
}
}
return;
}
void XLineManager::zline_set_creation_time(const char* ip, time_t create_time)
{
for (std::vector<ZLine*>::iterator i = zlines.begin(); i != zlines.end(); i++)
{
if (!strcasecmp(ip,(*i)->ipaddr))
{
(*i)->set_time = create_time;
(*i)->expiry = create_time + (*i)->duration;
return;
}
}
for (std::vector<ZLine*>::iterator i = pzlines.begin(); i != pzlines.end(); i++)
{
if (!strcasecmp(ip,(*i)->ipaddr))
{
(*i)->set_time = create_time;
return;
}
}
return;
}
// returns a pointer to the reason if an ip address matches a zline, NULL if it didnt match
ZLine* XLineManager::matches_zline(const char* ipaddr, bool permonly)
{
if ((zlines.empty()) && (pzlines.empty()))
return NULL;
if (!permonly)
{
for (std::vector<ZLine*>::iterator i = zlines.begin(); i != zlines.end(); i++)
if (match(ipaddr,(*i)->ipaddr, true))
return (*i);
}
for (std::vector<ZLine*>::iterator i = pzlines.begin(); i != pzlines.end(); i++)
if (match(ipaddr,(*i)->ipaddr, true))
return (*i);
return NULL;
}
// returns a pointer to the reason if a host matches a kline, NULL if it didnt match
KLine* XLineManager::matches_kline(userrec* user, bool permonly)
{
if ((klines.empty()) && (pklines.empty()))
return NULL;
if (!permonly)
{
for (std::vector<KLine*>::iterator i = klines.begin(); i != klines.end(); i++)
{
if ((match(user->ident,(*i)->identmask)))
{
if ((match(user->host,(*i)->hostmask, true)) || (match(user->GetIPString(),(*i)->hostmask, true)))
{
return (*i);
}
}
}
}
for (std::vector<KLine*>::iterator i = pklines.begin(); i != pklines.end(); i++)
{
if ((match(user->ident,(*i)->identmask)))
{
if ((match(user->host,(*i)->hostmask, true)) || (match(user->GetIPString(),(*i)->hostmask, true)))
{
return (*i);
}
}
}
return NULL;
}
bool XLineManager::GSortComparison ( const GLine* one, const GLine* two )
{
return (one->expiry) < (two->expiry);
}
bool XLineManager::ESortComparison ( const ELine* one, const ELine* two )
{
return (one->expiry) < (two->expiry);
}
bool XLineManager::ZSortComparison ( const ZLine* one, const ZLine* two )
{
return (one->expiry) < (two->expiry);
}
bool XLineManager::KSortComparison ( const KLine* one, const KLine* two )
{
return (one->expiry) < (two->expiry);
}
bool XLineManager::QSortComparison ( const QLine* one, const QLine* two )
{
return (one->expiry) < (two->expiry);
}
// removes lines that have expired
void XLineManager::expire_lines()
{
time_t current = ServerInstance->Time();
/* Because we now store all our XLines in sorted order using ((*i)->duration + (*i)->set_time) as a key, this
* means that to expire the XLines we just need to do a while, picking off the top few until there are
* none left at the head of the queue that are after the current time.
*/
while ((glines.size()) && (current > (*glines.begin())->expiry))
{
std::vector<GLine*>::iterator i = glines.begin();
ServerInstance->SNO->WriteToSnoMask('x',"Expiring timed G-Line %s@%s (set by %s %d seconds ago)",(*i)->identmask,(*i)->hostmask,(*i)->source,(*i)->duration);
glines.erase(i);
}
while ((elines.size()) && (current > (*elines.begin())->expiry))
{
std::vector<ELine*>::iterator i = elines.begin();
ServerInstance->SNO->WriteToSnoMask('x',"Expiring timed E-Line %s@%s (set by %s %d seconds ago)",(*i)->identmask,(*i)->hostmask,(*i)->source,(*i)->duration);
elines.erase(i);
}
while ((zlines.size()) && (current > (*zlines.begin())->expiry))
{
std::vector<ZLine*>::iterator i = zlines.begin();
ServerInstance->SNO->WriteToSnoMask('x',"Expiring timed Z-Line %s (set by %s %d seconds ago)",(*i)->ipaddr,(*i)->source,(*i)->duration);
zlines.erase(i);
}
while ((klines.size()) && (current > (*klines.begin())->expiry))
{
std::vector<KLine*>::iterator i = klines.begin();
ServerInstance->SNO->WriteToSnoMask('x',"Expiring timed K-Line %s@%s (set by %s %d seconds ago)",(*i)->identmask,(*i)->hostmask,(*i)->source,(*i)->duration);
klines.erase(i);
}
while ((qlines.size()) && (current > (*qlines.begin())->expiry))
{
std::vector<QLine*>::iterator i = qlines.begin();
ServerInstance->SNO->WriteToSnoMask('x',"Expiring timed Q-Line %s (set by %s %d seconds ago)",(*i)->nick,(*i)->source,(*i)->duration);
qlines.erase(i);
}
}
// applies lines, removing clients and changing nicks etc as applicable
void XLineManager::apply_lines(const int What)
{
if (!What)
return;
if (What & APPLY_PERM_ONLY)
{
char reason[MAXBUF];
if ((!pglines.size()) && (!pklines.size()) && (!pzlines.size()) && (!pqlines.size()))
return;
XLine* check = NULL;
for (std::vector<userrec*>::const_iterator u2 = ServerInstance->local_users.begin(); u2 != ServerInstance->local_users.end(); u2++)
{
userrec* u = (userrec*)(*u2);
if (elines.size() || pelines.size())
if (matches_exception(u))
continue;
if ((What & APPLY_GLINES) && pglines.size())
{
if ((check = matches_gline(u,true)))
{
snprintf(reason,MAXBUF,"G-Lined: %s",check->reason);
if (*ServerInstance->Config->MoronBanner)
u->WriteServ("NOTICE %s :*** %s", u->nick, ServerInstance->Config->MoronBanner);
if (ServerInstance->Config->HideBans)
ServerInstance->GlobalCulls.AddItem(u, "G-Lined", reason);
else
ServerInstance->GlobalCulls.AddItem(u, reason);
}
}
if ((What & APPLY_KLINES) && pklines.size())
{
if ((check = matches_kline(u,true)))
{
snprintf(reason,MAXBUF,"K-Lined: %s",check->reason);
if (*ServerInstance->Config->MoronBanner)
u->WriteServ("NOTICE %s :*** %s", u->nick, ServerInstance->Config->MoronBanner);
if (ServerInstance->Config->HideBans)
ServerInstance->GlobalCulls.AddItem(u, "K-Lined", reason);
else
ServerInstance->GlobalCulls.AddItem(u, reason);
}
}
if ((What & APPLY_QLINES) && pqlines.size())
{
if ((check = matches_qline(u->nick,true)))
{
snprintf(reason,MAXBUF,"Q-Lined: %s",check->reason);
if (*ServerInstance->Config->MoronBanner)
u->WriteServ("NOTICE %s :*** %s", u->nick, ServerInstance->Config->MoronBanner);
if (ServerInstance->Config->HideBans)
ServerInstance->GlobalCulls.AddItem(u, "Q-Lined", reason);
else
ServerInstance->GlobalCulls.AddItem(u, reason);
}
}
if ((What & APPLY_ZLINES) && pzlines.size())
{
if ((check = matches_zline(u->GetIPString(),true)))
{
snprintf(reason,MAXBUF,"Z-Lined: %s",check->reason);
if (*ServerInstance->Config->MoronBanner)
u->WriteServ("NOTICE %s :*** %s", u->nick, ServerInstance->Config->MoronBanner);
if (ServerInstance->Config->HideBans)
ServerInstance->GlobalCulls.AddItem(u, "Z-Lined", reason);
else
ServerInstance->GlobalCulls.AddItem(u, reason);
}
}
}
}
else
{
char reason[MAXBUF];
if ((!glines.size()) && (!klines.size()) && (!zlines.size()) && (!qlines.size()) &&
(!pglines.size()) && (!pklines.size()) && (!pzlines.size()) && (!pqlines.size()))
return;
XLine* check = NULL;
for (std::vector<userrec*>::const_iterator u2 = ServerInstance->local_users.begin(); u2 != ServerInstance->local_users.end(); u2++)
{
userrec* u = (userrec*)(*u2);
if (elines.size() || pelines.size())
{
// ignore people matching exempts
if (matches_exception(u))
continue;
}
if ((What & APPLY_GLINES) && (glines.size() || pglines.size()))
{
if ((check = matches_gline(u)))
{
snprintf(reason,MAXBUF,"G-Lined: %s",check->reason);
if (*ServerInstance->Config->MoronBanner)
u->WriteServ("NOTICE %s :*** %s", u->nick, ServerInstance->Config->MoronBanner);
if (ServerInstance->Config->HideBans)
ServerInstance->GlobalCulls.AddItem(u, "G-Lined", reason);
else
ServerInstance->GlobalCulls.AddItem(u, reason);
}
}
if ((What & APPLY_KLINES) && (klines.size() || pklines.size()))
{
if ((check = matches_kline(u)))
{
snprintf(reason,MAXBUF,"K-Lined: %s",check->reason);
if (*ServerInstance->Config->MoronBanner)
u->WriteServ("NOTICE %s :*** %s", u->nick, ServerInstance->Config->MoronBanner);
if (ServerInstance->Config->HideBans)
ServerInstance->GlobalCulls.AddItem(u, "K-Lined", reason);
else
ServerInstance->GlobalCulls.AddItem(u, reason);
}
}
if ((What & APPLY_QLINES) && (qlines.size() || pqlines.size()))
{
if ((check = matches_qline(u->nick)))
{
snprintf(reason,MAXBUF,"Q-Lined: %s",check->reason);
if (*ServerInstance->Config->MoronBanner)
u->WriteServ("NOTICE %s :*** %s", u->nick, ServerInstance->Config->MoronBanner);
if (ServerInstance->Config->HideBans)
ServerInstance->GlobalCulls.AddItem(u, "Q-Lined", reason);
else
ServerInstance->GlobalCulls.AddItem(u, reason);
}
}
if ((What & APPLY_ZLINES) && (zlines.size() || pzlines.size()))
{
if ((check = matches_zline(u->GetIPString())))
{
snprintf(reason,MAXBUF,"Z-Lined: %s", check->reason);
if (*ServerInstance->Config->MoronBanner)
u->WriteServ("NOTICE %s :*** %s", u->nick, ServerInstance->Config->MoronBanner);
if (ServerInstance->Config->HideBans)
ServerInstance->GlobalCulls.AddItem(u, "Z-Lined", reason);
else
ServerInstance->GlobalCulls.AddItem(u, reason);
}
}
}
}
}
void XLineManager::stats_k(userrec* user, string_list &results)
{
std::string sn = ServerInstance->Config->ServerName;
for (std::vector<KLine*>::iterator i = klines.begin(); i != klines.end(); i++)
results.push_back(sn+" 216 "+user->nick+" :"+(*i)->identmask+"@"+(*i)->hostmask+" "+ConvToStr((*i)->set_time)+" "+ConvToStr((*i)->duration)+" "+(*i)->source+" :"+(*i)->reason);
for (std::vector<KLine*>::iterator i = pklines.begin(); i != pklines.end(); i++)
results.push_back(sn+" 216 "+user->nick+" :"+(*i)->identmask+"@"+(*i)->hostmask+" "+ConvToStr((*i)->set_time)+" "+ConvToStr((*i)->duration)+" "+(*i)->source+" :"+(*i)->reason);
}
void XLineManager::stats_g(userrec* user, string_list &results)
{
std::string sn = ServerInstance->Config->ServerName;
for (std::vector<GLine*>::iterator i = glines.begin(); i != glines.end(); i++)
results.push_back(sn+" 223 "+user->nick+" :"+(*i)->identmask+"@"+(*i)->hostmask+" "+ConvToStr((*i)->set_time)+" "+ConvToStr((*i)->duration)+" "+(*i)->source+" :"+(*i)->reason);
for (std::vector<GLine*>::iterator i = pglines.begin(); i != pglines.end(); i++)
results.push_back(sn+" 223 "+user->nick+" :"+(*i)->identmask+"@"+(*i)->hostmask+" "+ConvToStr((*i)->set_time)+" "+ConvToStr((*i)->duration)+" "+(*i)->source+" :"+(*i)->reason);
}
void XLineManager::stats_q(userrec* user, string_list &results)
{
std::string sn = ServerInstance->Config->ServerName;
for (std::vector<QLine*>::iterator i = qlines.begin(); i != qlines.end(); i++)
results.push_back(sn+" 217 "+user->nick+" :"+(*i)->nick+" "+ConvToStr((*i)->set_time)+" "+ConvToStr((*i)->duration)+" "+(*i)->source+" :"+(*i)->reason);
for (std::vector<QLine*>::iterator i = pqlines.begin(); i != pqlines.end(); i++)
results.push_back(sn+" 217 "+user->nick+" :"+(*i)->nick+" "+ConvToStr((*i)->set_time)+" "+ConvToStr((*i)->duration)+" "+(*i)->source+" :"+(*i)->reason);
}
void XLineManager::stats_z(userrec* user, string_list &results)
{
std::string sn = ServerInstance->Config->ServerName;
for (std::vector<ZLine*>::iterator i = zlines.begin(); i != zlines.end(); i++)
results.push_back(sn+" 223 "+user->nick+" :"+(*i)->ipaddr+" "+ConvToStr((*i)->set_time)+" "+ConvToStr((*i)->duration)+" "+(*i)->source+" :"+(*i)->reason);
for (std::vector<ZLine*>::iterator i = pzlines.begin(); i != pzlines.end(); i++)
results.push_back(sn+" 223 "+user->nick+" :"+(*i)->ipaddr+" "+ConvToStr((*i)->set_time)+" "+ConvToStr((*i)->duration)+" "+(*i)->source+" :"+(*i)->reason);
}
void XLineManager::stats_e(userrec* user, string_list &results)
{
std::string sn = ServerInstance->Config->ServerName;
for (std::vector<ELine*>::iterator i = elines.begin(); i != elines.end(); i++)
results.push_back(sn+" 223 "+user->nick+" :"+(*i)->identmask+"@"+(*i)->hostmask+" "+ConvToStr((*i)->set_time)+" "+ConvToStr((*i)->duration)+" "+(*i)->source+" :"+(*i)->reason);
for (std::vector<ELine*>::iterator i = pelines.begin(); i != pelines.end(); i++)
results.push_back(sn+" 223 "+user->nick+" :"+(*i)->identmask+"@"+(*i)->hostmask+" "+ConvToStr((*i)->set_time)+" "+ConvToStr((*i)->duration)+" "+(*i)->source+" :"+(*i)->reason);
}
XLineManager::XLineManager(InspIRCd* Instance) : ServerInstance(Instance)
{
}
\ No newline at end of file +/* +------------------------------------+ + * | 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 "users.h" +#include "modules.h" +#include "wildcard.h" +#include "xline.h" + +/* Version two, now with optimized expiry! + * + * Because the old way was horrendously slow, the new way of expiring xlines is very + * very efficient. I have improved the efficiency of the algorithm in two ways: + * + * (1) There are now two lists of items for each linetype. One list holds temporary + * items, and the other list holds permanent items (ones which will expire). + * Items which are on the permanent list are NEVER checked at all by the + * expire_lines() function. + * (2) The temporary xline lists are always kept in strict numerical order, keyed by + * current time + duration. This means that the line which is due to expire the + * soonest is always pointed at by vector::begin(), so a simple while loop can + * very efficiently, very quickly and above all SAFELY pick off the first few + * items in the vector which need zapping. + * + * -- Brain + */ + +bool InitXLine(ServerConfig* conf, const char* tag) +{ + return true; +} + +bool DoneZLine(ServerConfig* conf, const char* tag) +{ + conf->GetInstance()->XLines->apply_lines(APPLY_ZLINES|APPLY_PERM_ONLY); + return true; +} + +bool DoneQLine(ServerConfig* conf, const char* tag) +{ + conf->GetInstance()->XLines->apply_lines(APPLY_QLINES|APPLY_PERM_ONLY); + return true; +} + +bool DoneKLine(ServerConfig* conf, const char* tag) +{ + conf->GetInstance()->XLines->apply_lines(APPLY_KLINES|APPLY_PERM_ONLY); + return true; +} + +bool DoneELine(ServerConfig* conf, const char* tag) +{ + /* Yes, this is supposed to do nothing, we dont 'apply' these */ + return true; +} + +bool DoZLine(ServerConfig* conf, const char* tag, char** entries, ValueList &values, int* types) +{ + const char* reason = values[0].GetString(); + const char* ipmask = values[1].GetString(); + + conf->GetInstance()->XLines->add_zline(0,"<Config>",reason,ipmask); + return true; +} + +bool DoQLine(ServerConfig* conf, const char* tag, char** entries, ValueList &values, int* types) +{ + const char* reason = values[0].GetString(); + const char* nick = values[1].GetString(); + + conf->GetInstance()->XLines->add_qline(0,"<Config>",reason,nick); + return true; +} + +bool DoKLine(ServerConfig* conf, const char* tag, char** entries, ValueList &values, int* types) +{ + const char* reason = values[0].GetString(); + const char* host = values[1].GetString(); + + conf->GetInstance()->XLines->add_kline(0,"<Config>",reason,host); + return true; +} + +bool DoELine(ServerConfig* conf, const char* tag, char** entries, ValueList &values, int* types) +{ + const char* reason = values[0].GetString(); + const char* host = values[1].GetString(); + + conf->GetInstance()->XLines->add_eline(0,"<Config>",reason,host); + return true; +} + +IdentHostPair XLineManager::IdentSplit(const std::string &ident_and_host) +{ + IdentHostPair n = std::make_pair<std::string,std::string>("*","*"); + std::string::size_type x = ident_and_host.find('@'); + if (x != std::string::npos) + { + n.second = ident_and_host.substr(x + 1,ident_and_host.length()); + n.first = ident_and_host.substr(0, x); + if (!n.first.length()) + n.first.assign("*"); + if (!n.second.length()) + n.second.assign("*"); + } + else + { + n.second = ident_and_host; + } + + return n; +} + +// adds a g:line + +bool XLineManager::add_gline(long duration, const char* source,const char* reason,const char* hostmask) +{ + IdentHostPair ih = IdentSplit(hostmask); + + if (del_gline(hostmask, true)) + return false; + + GLine* item = new GLine(ServerInstance->Time(), duration, source, reason, ih.first.c_str(), ih.second.c_str()); + + if (duration) + { + glines.push_back(item); + sort(glines.begin(), glines.end(),XLineManager::GSortComparison); + } + else + { + pglines.push_back(item); + } + + return true; +} + +// adds an e:line (exception to bans) + +bool XLineManager::add_eline(long duration, const char* source, const char* reason, const char* hostmask) +{ + IdentHostPair ih = IdentSplit(hostmask); + + if (del_eline(hostmask, true)) + return false; + + ELine* item = new ELine(ServerInstance->Time(), duration, source, reason, ih.first.c_str(), ih.second.c_str()); + + if (duration) + { + elines.push_back(item); + sort(elines.begin(), elines.end(),XLineManager::ESortComparison); + } + else + { + pelines.push_back(item); + } + return true; +} + +// adds a q:line + +bool XLineManager::add_qline(long duration, const char* source, const char* reason, const char* nickname) +{ + if (del_qline(nickname, true)) + return false; + + QLine* item = new QLine(ServerInstance->Time(), duration, source, reason, nickname); + + if (duration) + { + qlines.push_back(item); + sort(qlines.begin(), qlines.end(),XLineManager::QSortComparison); + } + else + { + pqlines.push_back(item); + } + return true; +} + +// adds a z:line + +bool XLineManager::add_zline(long duration, const char* source, const char* reason, const char* ipaddr) +{ + if (strchr(ipaddr,'@')) + { + while (*ipaddr != '@') + ipaddr++; + ipaddr++; + } + + if (del_zline(ipaddr, true)) + return false; + + ZLine* item = new ZLine(ServerInstance->Time(), duration, source, reason, ipaddr); + + if (duration) + { + zlines.push_back(item); + sort(zlines.begin(), zlines.end(),XLineManager::ZSortComparison); + } + else + { + pzlines.push_back(item); + } + return true; +} + +// adds a k:line + +bool XLineManager::add_kline(long duration, const char* source, const char* reason, const char* hostmask) +{ + IdentHostPair ih = IdentSplit(hostmask); + + if (del_kline(hostmask, true)) + return false; + + KLine* item = new KLine(ServerInstance->Time(), duration, source, reason, ih.first.c_str(), ih.second.c_str()); + + if (duration) + { + klines.push_back(item); + sort(klines.begin(), klines.end(),XLineManager::KSortComparison); + } + else + { + pklines.push_back(item); + } + return true; +} + +// deletes a g:line, returns true if the line existed and was removed + +bool XLineManager::del_gline(const char* hostmask, bool simulate) +{ + IdentHostPair ih = IdentSplit(hostmask); + for (std::vector<GLine*>::iterator i = glines.begin(); i != glines.end(); i++) + { + if (!strcasecmp(ih.first.c_str(),(*i)->identmask) && !strcasecmp(ih.second.c_str(),(*i)->hostmask)) + { + if (!simulate) + { + delete *i; + glines.erase(i); + } + return true; + } + } + for (std::vector<GLine*>::iterator i = pglines.begin(); i != pglines.end(); i++) + { + if (!strcasecmp(ih.first.c_str(),(*i)->identmask) && !strcasecmp(ih.second.c_str(),(*i)->hostmask)) + { + if (!simulate) + { + delete *i; + pglines.erase(i); + } + return true; + } + } + return false; +} + +// deletes a e:line, returns true if the line existed and was removed + +bool XLineManager::del_eline(const char* hostmask, bool simulate) +{ + IdentHostPair ih = IdentSplit(hostmask); + for (std::vector<ELine*>::iterator i = elines.begin(); i != elines.end(); i++) + { + if (!strcasecmp(ih.first.c_str(),(*i)->identmask) && !strcasecmp(ih.second.c_str(),(*i)->hostmask)) + { + if (!simulate) + { + delete *i; + elines.erase(i); + } + return true; + } + } + for (std::vector<ELine*>::iterator i = pelines.begin(); i != pelines.end(); i++) + { + if (!strcasecmp(ih.first.c_str(),(*i)->identmask) && !strcasecmp(ih.second.c_str(),(*i)->hostmask)) + { + if (!simulate) + { + delete *i; + pelines.erase(i); + } + return true; + } + } + return false; +} + +// deletes a q:line, returns true if the line existed and was removed + +bool XLineManager::del_qline(const char* nickname, bool simulate) +{ + for (std::vector<QLine*>::iterator i = qlines.begin(); i != qlines.end(); i++) + { + if (!strcasecmp(nickname,(*i)->nick)) + { + if (!simulate) + { + delete *i; + qlines.erase(i); + } + return true; + } + } + for (std::vector<QLine*>::iterator i = pqlines.begin(); i != pqlines.end(); i++) + { + if (!strcasecmp(nickname,(*i)->nick)) + { + if (!simulate) + { + delete *i; + pqlines.erase(i); + } + return true; + } + } + return false; +} + +// deletes a z:line, returns true if the line existed and was removed + +bool XLineManager::del_zline(const char* ipaddr, bool simulate) +{ + for (std::vector<ZLine*>::iterator i = zlines.begin(); i != zlines.end(); i++) + { + if (!strcasecmp(ipaddr,(*i)->ipaddr)) + { + if (!simulate) + { + delete *i; + zlines.erase(i); + } + return true; + } + } + for (std::vector<ZLine*>::iterator i = pzlines.begin(); i != pzlines.end(); i++) + { + if (!strcasecmp(ipaddr,(*i)->ipaddr)) + { + if (!simulate) + { + delete *i; + pzlines.erase(i); + } + return true; + } + } + return false; +} + +// deletes a k:line, returns true if the line existed and was removed + +bool XLineManager::del_kline(const char* hostmask, bool simulate) +{ + IdentHostPair ih = IdentSplit(hostmask); + for (std::vector<KLine*>::iterator i = klines.begin(); i != klines.end(); i++) + { + if (!strcasecmp(ih.first.c_str(),(*i)->identmask) && !strcasecmp(ih.second.c_str(),(*i)->hostmask)) + { + if (!simulate) + { + delete *i; + klines.erase(i); + } + return true; + } + } + for (std::vector<KLine*>::iterator i = pklines.begin(); i != pklines.end(); i++) + { + if (!strcasecmp(ih.first.c_str(),(*i)->identmask) && !strcasecmp(ih.second.c_str(),(*i)->hostmask)) + { + if (!simulate) + { + delete *i; + pklines.erase(i); + } + return true; + } + } + return false; +} + +// returns a pointer to the reason if a nickname matches a qline, NULL if it didnt match + +QLine* XLineManager::matches_qline(const char* nick, bool permonly) +{ + if ((qlines.empty()) && (pqlines.empty())) + return NULL; + if (!permonly) + { + for (std::vector<QLine*>::iterator i = qlines.begin(); i != qlines.end(); i++) + if (match(nick,(*i)->nick)) + return (*i); + } + for (std::vector<QLine*>::iterator i = pqlines.begin(); i != pqlines.end(); i++) + if (match(nick,(*i)->nick)) + return (*i); + return NULL; +} + +// returns a pointer to the reason if a host matches a gline, NULL if it didnt match + +GLine* XLineManager::matches_gline(userrec* user, bool permonly) +{ + if ((glines.empty()) && (pglines.empty())) + return NULL; + if (!permonly) + { + for (std::vector<GLine*>::iterator i = glines.begin(); i != glines.end(); i++) + { + if ((match(user->ident,(*i)->identmask))) + { + if ((match(user->host,(*i)->hostmask, true)) || (match(user->GetIPString(),(*i)->hostmask, true))) + { + return (*i); + } + } + } + } + for (std::vector<GLine*>::iterator i = pglines.begin(); i != pglines.end(); i++) + { + if ((match(user->ident,(*i)->identmask))) + { + if ((match(user->host,(*i)->hostmask, true)) || (match(user->GetIPString(),(*i)->hostmask, true))) + { + return (*i); + } + } + } + return NULL; +} + +ELine* XLineManager::matches_exception(userrec* user, bool permonly) +{ + if ((elines.empty()) && (pelines.empty())) + return NULL; + char host2[MAXBUF]; + snprintf(host2,MAXBUF,"*@%s",user->host); + if (!permonly) + { + for (std::vector<ELine*>::iterator i = elines.begin(); i != elines.end(); i++) + { + if ((match(user->ident,(*i)->identmask))) + { + if ((match(user->host,(*i)->hostmask, true)) || (match(user->GetIPString(),(*i)->hostmask, true))) + { + return (*i); + } + } + } + } + for (std::vector<ELine*>::iterator i = pelines.begin(); i != pelines.end(); i++) + { + if ((match(user->ident,(*i)->identmask))) + { + if ((match(user->host,(*i)->hostmask, true)) || (match(user->GetIPString(),(*i)->hostmask, true))) + { + return (*i); + } + } + } + return NULL; +} + + +void XLineManager::gline_set_creation_time(const char* host, time_t create_time) +{ + for (std::vector<GLine*>::iterator i = glines.begin(); i != glines.end(); i++) + { + if (!strcasecmp(host,(*i)->hostmask)) + { + (*i)->set_time = create_time; + (*i)->expiry = create_time + (*i)->duration; + return; + } + } + for (std::vector<GLine*>::iterator i = pglines.begin(); i != pglines.end(); i++) + { + if (!strcasecmp(host,(*i)->hostmask)) + { + (*i)->set_time = create_time; + return; + } + } + return ; +} + +void XLineManager::eline_set_creation_time(const char* host, time_t create_time) +{ + for (std::vector<ELine*>::iterator i = elines.begin(); i != elines.end(); i++) + { + if (!strcasecmp(host,(*i)->hostmask)) + { + (*i)->set_time = create_time; + (*i)->expiry = create_time + (*i)->duration; + return; + } + } + for (std::vector<ELine*>::iterator i = pelines.begin(); i != pelines.end(); i++) + { + if (!strcasecmp(host,(*i)->hostmask)) + { + (*i)->set_time = create_time; + return; + } + } + return; +} + +void XLineManager::qline_set_creation_time(const char* nick, time_t create_time) +{ + for (std::vector<QLine*>::iterator i = qlines.begin(); i != qlines.end(); i++) + { + if (!strcasecmp(nick,(*i)->nick)) + { + (*i)->set_time = create_time; + (*i)->expiry = create_time + (*i)->duration; + return; + } + } + for (std::vector<QLine*>::iterator i = pqlines.begin(); i != pqlines.end(); i++) + { + if (!strcasecmp(nick,(*i)->nick)) + { + (*i)->set_time = create_time; + return; + } + } + return; +} + +void XLineManager::zline_set_creation_time(const char* ip, time_t create_time) +{ + for (std::vector<ZLine*>::iterator i = zlines.begin(); i != zlines.end(); i++) + { + if (!strcasecmp(ip,(*i)->ipaddr)) + { + (*i)->set_time = create_time; + (*i)->expiry = create_time + (*i)->duration; + return; + } + } + for (std::vector<ZLine*>::iterator i = pzlines.begin(); i != pzlines.end(); i++) + { + if (!strcasecmp(ip,(*i)->ipaddr)) + { + (*i)->set_time = create_time; + return; + } + } + return; +} + +// returns a pointer to the reason if an ip address matches a zline, NULL if it didnt match + +ZLine* XLineManager::matches_zline(const char* ipaddr, bool permonly) +{ + if ((zlines.empty()) && (pzlines.empty())) + return NULL; + if (!permonly) + { + for (std::vector<ZLine*>::iterator i = zlines.begin(); i != zlines.end(); i++) + if (match(ipaddr,(*i)->ipaddr, true)) + return (*i); + } + for (std::vector<ZLine*>::iterator i = pzlines.begin(); i != pzlines.end(); i++) + if (match(ipaddr,(*i)->ipaddr, true)) + return (*i); + return NULL; +} + +// returns a pointer to the reason if a host matches a kline, NULL if it didnt match + +KLine* XLineManager::matches_kline(userrec* user, bool permonly) +{ + if ((klines.empty()) && (pklines.empty())) + return NULL; + if (!permonly) + { + for (std::vector<KLine*>::iterator i = klines.begin(); i != klines.end(); i++) + { + if ((match(user->ident,(*i)->identmask))) + { + if ((match(user->host,(*i)->hostmask, true)) || (match(user->GetIPString(),(*i)->hostmask, true))) + { + return (*i); + } + } + } + } + for (std::vector<KLine*>::iterator i = pklines.begin(); i != pklines.end(); i++) + { + if ((match(user->ident,(*i)->identmask))) + { + if ((match(user->host,(*i)->hostmask, true)) || (match(user->GetIPString(),(*i)->hostmask, true))) + { + return (*i); + } + } + } + return NULL; +} + +bool XLineManager::GSortComparison ( const GLine* one, const GLine* two ) +{ + return (one->expiry) < (two->expiry); +} + +bool XLineManager::ESortComparison ( const ELine* one, const ELine* two ) +{ + return (one->expiry) < (two->expiry); +} + +bool XLineManager::ZSortComparison ( const ZLine* one, const ZLine* two ) +{ + return (one->expiry) < (two->expiry); +} + +bool XLineManager::KSortComparison ( const KLine* one, const KLine* two ) +{ + return (one->expiry) < (two->expiry); +} + +bool XLineManager::QSortComparison ( const QLine* one, const QLine* two ) +{ + return (one->expiry) < (two->expiry); +} + +// removes lines that have expired + +void XLineManager::expire_lines() +{ + time_t current = ServerInstance->Time(); + + /* Because we now store all our XLines in sorted order using ((*i)->duration + (*i)->set_time) as a key, this + * means that to expire the XLines we just need to do a while, picking off the top few until there are + * none left at the head of the queue that are after the current time. + */ + + while ((glines.size()) && (current > (*glines.begin())->expiry)) + { + std::vector<GLine*>::iterator i = glines.begin(); + ServerInstance->SNO->WriteToSnoMask('x',"Expiring timed G-Line %s@%s (set by %s %d seconds ago)",(*i)->identmask,(*i)->hostmask,(*i)->source,(*i)->duration); + glines.erase(i); + } + + while ((elines.size()) && (current > (*elines.begin())->expiry)) + { + std::vector<ELine*>::iterator i = elines.begin(); + ServerInstance->SNO->WriteToSnoMask('x',"Expiring timed E-Line %s@%s (set by %s %d seconds ago)",(*i)->identmask,(*i)->hostmask,(*i)->source,(*i)->duration); + elines.erase(i); + } + + while ((zlines.size()) && (current > (*zlines.begin())->expiry)) + { + std::vector<ZLine*>::iterator i = zlines.begin(); + ServerInstance->SNO->WriteToSnoMask('x',"Expiring timed Z-Line %s (set by %s %d seconds ago)",(*i)->ipaddr,(*i)->source,(*i)->duration); + zlines.erase(i); + } + + while ((klines.size()) && (current > (*klines.begin())->expiry)) + { + std::vector<KLine*>::iterator i = klines.begin(); + ServerInstance->SNO->WriteToSnoMask('x',"Expiring timed K-Line %s@%s (set by %s %d seconds ago)",(*i)->identmask,(*i)->hostmask,(*i)->source,(*i)->duration); + klines.erase(i); + } + + while ((qlines.size()) && (current > (*qlines.begin())->expiry)) + { + std::vector<QLine*>::iterator i = qlines.begin(); + ServerInstance->SNO->WriteToSnoMask('x',"Expiring timed Q-Line %s (set by %s %d seconds ago)",(*i)->nick,(*i)->source,(*i)->duration); + qlines.erase(i); + } + +} + +// applies lines, removing clients and changing nicks etc as applicable + +void XLineManager::apply_lines(const int What) +{ + if (!What) + return; + + if (What & APPLY_PERM_ONLY) + { + char reason[MAXBUF]; + + if ((!pglines.size()) && (!pklines.size()) && (!pzlines.size()) && (!pqlines.size())) + return; + + XLine* check = NULL; + for (std::vector<userrec*>::const_iterator u2 = ServerInstance->local_users.begin(); u2 != ServerInstance->local_users.end(); u2++) + { + userrec* u = (userrec*)(*u2); + + if (elines.size() || pelines.size()) + if (matches_exception(u)) + continue; + + if ((What & APPLY_GLINES) && pglines.size()) + { + if ((check = matches_gline(u,true))) + { + snprintf(reason,MAXBUF,"G-Lined: %s",check->reason); + if (*ServerInstance->Config->MoronBanner) + u->WriteServ("NOTICE %s :*** %s", u->nick, ServerInstance->Config->MoronBanner); + if (ServerInstance->Config->HideBans) + ServerInstance->GlobalCulls.AddItem(u, "G-Lined", reason); + else + ServerInstance->GlobalCulls.AddItem(u, reason); + } + } + + if ((What & APPLY_KLINES) && pklines.size()) + { + if ((check = matches_kline(u,true))) + { + snprintf(reason,MAXBUF,"K-Lined: %s",check->reason); + if (*ServerInstance->Config->MoronBanner) + u->WriteServ("NOTICE %s :*** %s", u->nick, ServerInstance->Config->MoronBanner); + if (ServerInstance->Config->HideBans) + ServerInstance->GlobalCulls.AddItem(u, "K-Lined", reason); + else + ServerInstance->GlobalCulls.AddItem(u, reason); + } + } + + if ((What & APPLY_QLINES) && pqlines.size()) + { + if ((check = matches_qline(u->nick,true))) + { + snprintf(reason,MAXBUF,"Q-Lined: %s",check->reason); + if (*ServerInstance->Config->MoronBanner) + u->WriteServ("NOTICE %s :*** %s", u->nick, ServerInstance->Config->MoronBanner); + if (ServerInstance->Config->HideBans) + ServerInstance->GlobalCulls.AddItem(u, "Q-Lined", reason); + else + ServerInstance->GlobalCulls.AddItem(u, reason); + } + } + + if ((What & APPLY_ZLINES) && pzlines.size()) + { + if ((check = matches_zline(u->GetIPString(),true))) + { + snprintf(reason,MAXBUF,"Z-Lined: %s",check->reason); + if (*ServerInstance->Config->MoronBanner) + u->WriteServ("NOTICE %s :*** %s", u->nick, ServerInstance->Config->MoronBanner); + if (ServerInstance->Config->HideBans) + ServerInstance->GlobalCulls.AddItem(u, "Z-Lined", reason); + else + ServerInstance->GlobalCulls.AddItem(u, reason); + } + } + } + } + else + { + char reason[MAXBUF]; + + if ((!glines.size()) && (!klines.size()) && (!zlines.size()) && (!qlines.size()) && + (!pglines.size()) && (!pklines.size()) && (!pzlines.size()) && (!pqlines.size())) + return; + + XLine* check = NULL; + for (std::vector<userrec*>::const_iterator u2 = ServerInstance->local_users.begin(); u2 != ServerInstance->local_users.end(); u2++) + { + userrec* u = (userrec*)(*u2); + + if (elines.size() || pelines.size()) + { + // ignore people matching exempts + if (matches_exception(u)) + continue; + } + if ((What & APPLY_GLINES) && (glines.size() || pglines.size())) + { + if ((check = matches_gline(u))) + { + snprintf(reason,MAXBUF,"G-Lined: %s",check->reason); + if (*ServerInstance->Config->MoronBanner) + u->WriteServ("NOTICE %s :*** %s", u->nick, ServerInstance->Config->MoronBanner); + if (ServerInstance->Config->HideBans) + ServerInstance->GlobalCulls.AddItem(u, "G-Lined", reason); + else + ServerInstance->GlobalCulls.AddItem(u, reason); + } + } + if ((What & APPLY_KLINES) && (klines.size() || pklines.size())) + { + if ((check = matches_kline(u))) + { + snprintf(reason,MAXBUF,"K-Lined: %s",check->reason); + if (*ServerInstance->Config->MoronBanner) + u->WriteServ("NOTICE %s :*** %s", u->nick, ServerInstance->Config->MoronBanner); + if (ServerInstance->Config->HideBans) + ServerInstance->GlobalCulls.AddItem(u, "K-Lined", reason); + else + ServerInstance->GlobalCulls.AddItem(u, reason); + } + } + if ((What & APPLY_QLINES) && (qlines.size() || pqlines.size())) + { + if ((check = matches_qline(u->nick))) + { + snprintf(reason,MAXBUF,"Q-Lined: %s",check->reason); + if (*ServerInstance->Config->MoronBanner) + u->WriteServ("NOTICE %s :*** %s", u->nick, ServerInstance->Config->MoronBanner); + if (ServerInstance->Config->HideBans) + ServerInstance->GlobalCulls.AddItem(u, "Q-Lined", reason); + else + ServerInstance->GlobalCulls.AddItem(u, reason); + } + } + if ((What & APPLY_ZLINES) && (zlines.size() || pzlines.size())) + { + if ((check = matches_zline(u->GetIPString()))) + { + snprintf(reason,MAXBUF,"Z-Lined: %s", check->reason); + if (*ServerInstance->Config->MoronBanner) + u->WriteServ("NOTICE %s :*** %s", u->nick, ServerInstance->Config->MoronBanner); + if (ServerInstance->Config->HideBans) + ServerInstance->GlobalCulls.AddItem(u, "Z-Lined", reason); + else + ServerInstance->GlobalCulls.AddItem(u, reason); + } + } + } + } +} + +void XLineManager::stats_k(userrec* user, string_list &results) +{ + std::string sn = ServerInstance->Config->ServerName; + for (std::vector<KLine*>::iterator i = klines.begin(); i != klines.end(); i++) + results.push_back(sn+" 216 "+user->nick+" :"+(*i)->identmask+"@"+(*i)->hostmask+" "+ConvToStr((*i)->set_time)+" "+ConvToStr((*i)->duration)+" "+(*i)->source+" :"+(*i)->reason); + for (std::vector<KLine*>::iterator i = pklines.begin(); i != pklines.end(); i++) + results.push_back(sn+" 216 "+user->nick+" :"+(*i)->identmask+"@"+(*i)->hostmask+" "+ConvToStr((*i)->set_time)+" "+ConvToStr((*i)->duration)+" "+(*i)->source+" :"+(*i)->reason); +} + +void XLineManager::stats_g(userrec* user, string_list &results) +{ + std::string sn = ServerInstance->Config->ServerName; + for (std::vector<GLine*>::iterator i = glines.begin(); i != glines.end(); i++) + results.push_back(sn+" 223 "+user->nick+" :"+(*i)->identmask+"@"+(*i)->hostmask+" "+ConvToStr((*i)->set_time)+" "+ConvToStr((*i)->duration)+" "+(*i)->source+" :"+(*i)->reason); + for (std::vector<GLine*>::iterator i = pglines.begin(); i != pglines.end(); i++) + results.push_back(sn+" 223 "+user->nick+" :"+(*i)->identmask+"@"+(*i)->hostmask+" "+ConvToStr((*i)->set_time)+" "+ConvToStr((*i)->duration)+" "+(*i)->source+" :"+(*i)->reason); +} + +void XLineManager::stats_q(userrec* user, string_list &results) +{ + std::string sn = ServerInstance->Config->ServerName; + for (std::vector<QLine*>::iterator i = qlines.begin(); i != qlines.end(); i++) + results.push_back(sn+" 217 "+user->nick+" :"+(*i)->nick+" "+ConvToStr((*i)->set_time)+" "+ConvToStr((*i)->duration)+" "+(*i)->source+" :"+(*i)->reason); + for (std::vector<QLine*>::iterator i = pqlines.begin(); i != pqlines.end(); i++) + results.push_back(sn+" 217 "+user->nick+" :"+(*i)->nick+" "+ConvToStr((*i)->set_time)+" "+ConvToStr((*i)->duration)+" "+(*i)->source+" :"+(*i)->reason); +} + +void XLineManager::stats_z(userrec* user, string_list &results) +{ + std::string sn = ServerInstance->Config->ServerName; + for (std::vector<ZLine*>::iterator i = zlines.begin(); i != zlines.end(); i++) + results.push_back(sn+" 223 "+user->nick+" :"+(*i)->ipaddr+" "+ConvToStr((*i)->set_time)+" "+ConvToStr((*i)->duration)+" "+(*i)->source+" :"+(*i)->reason); + for (std::vector<ZLine*>::iterator i = pzlines.begin(); i != pzlines.end(); i++) + results.push_back(sn+" 223 "+user->nick+" :"+(*i)->ipaddr+" "+ConvToStr((*i)->set_time)+" "+ConvToStr((*i)->duration)+" "+(*i)->source+" :"+(*i)->reason); +} + +void XLineManager::stats_e(userrec* user, string_list &results) +{ + std::string sn = ServerInstance->Config->ServerName; + for (std::vector<ELine*>::iterator i = elines.begin(); i != elines.end(); i++) + results.push_back(sn+" 223 "+user->nick+" :"+(*i)->identmask+"@"+(*i)->hostmask+" "+ConvToStr((*i)->set_time)+" "+ConvToStr((*i)->duration)+" "+(*i)->source+" :"+(*i)->reason); + for (std::vector<ELine*>::iterator i = pelines.begin(); i != pelines.end(); i++) + results.push_back(sn+" 223 "+user->nick+" :"+(*i)->identmask+"@"+(*i)->hostmask+" "+ConvToStr((*i)->set_time)+" "+ConvToStr((*i)->duration)+" "+(*i)->source+" :"+(*i)->reason); +} + +XLineManager::XLineManager(InspIRCd* Instance) : ServerInstance(Instance) +{ +} |