From bab14f0dd2345c9d7dcbc47c918563709e1ac094 Mon Sep 17 00:00:00 2001 From: peavey Date: Mon, 16 Jul 2007 17:14:45 +0000 Subject: 'svn propset -R svn:eol-style CR *' Set to UNIX-style always. Binaries are auto skipped by svn. git-svn-id: http://svn.inspircd.org/repository/trunk/inspircd@7454 e03df62e-2008-0410-955e-edbf42e46eb7 --- src/base.cpp | 96 +- src/channels.cpp | 1068 +------------ src/cmd_admin.cpp | 36 +- src/cmd_away.cpp | 43 +- src/cmd_clearcache.cpp | 32 +- src/cmd_commands.cpp | 34 +- src/cmd_connect.cpp | 34 +- src/cmd_die.cpp | 48 +- src/cmd_eline.cpp | 78 +- src/cmd_gline.cpp | 90 +- src/cmd_info.cpp | 76 +- src/cmd_invite.cpp | 99 +- src/cmd_ison.cpp | 87 +- src/cmd_join.cpp | 53 +- src/cmd_kick.cpp | 59 +- src/cmd_kill.cpp | 118 +- src/cmd_kline.cpp | 89 +- src/cmd_links.cpp | 33 +- src/cmd_list.cpp | 86 +- src/cmd_loadmodule.cpp | 40 +- src/cmd_lusers.cpp | 43 +- src/cmd_map.cpp | 36 +- src/cmd_mode.cpp | 32 +- src/cmd_modules.cpp | 76 +- src/cmd_motd.cpp | 30 +- src/cmd_names.cpp | 55 +- src/cmd_nick.cpp | 190 +-- src/cmd_notice.cpp | 159 +- src/cmd_oper.cpp | 154 +- src/cmd_part.cpp | 44 +- src/cmd_pass.cpp | 43 +- src/cmd_ping.cpp | 29 +- src/cmd_pong.cpp | 29 +- src/cmd_privmsg.cpp | 165 +- src/cmd_qline.cpp | 81 +- src/cmd_quit.cpp | 49 +- src/cmd_rehash.cpp | 57 +- src/cmd_reloadmodule.cpp | 40 +- src/cmd_restart.cpp | 50 +- src/cmd_rules.cpp | 28 +- src/cmd_server.cpp | 31 +- src/cmd_squit.cpp | 33 +- src/cmd_stats.cpp | 319 +--- src/cmd_summon.cpp | 28 +- src/cmd_time.cpp | 41 +- src/cmd_topic.cpp | 119 +- src/cmd_trace.cpp | 47 +- src/cmd_unloadmodule.cpp | 40 +- src/cmd_user.cpp | 70 +- src/cmd_userhost.cpp | 64 +- src/cmd_users.cpp | 28 +- src/cmd_version.cpp | 32 +- src/cmd_wallops.cpp | 32 +- src/cmd_who.cpp | 329 +--- src/cmd_whois.cpp | 145 +- src/cmd_whowas.cpp | 342 +---- src/cmd_zline.cpp | 81 +- src/command_parse.cpp | 564 +------ src/commands.cpp | 112 +- src/configreader.cpp | 1715 +-------------------- src/cull_list.cpp | 203 +-- src/dns.cpp | 1170 +------------- src/dynamic.cpp | 90 +- src/hashcomp.cpp | 620 +------- src/helperfuncs.cpp | 535 +------ src/inspircd.cpp | 1308 +--------------- src/inspsocket.cpp | 751 +-------- src/inspstring.cpp | 138 +- src/mode.cpp | 1067 +------------ src/modes/Makefile | 66 +- src/modes/cmode_b.cpp | 186 +-- src/modes/cmode_h.cpp | 163 +- src/modes/cmode_i.cpp | 36 +- src/modes/cmode_k.cpp | 104 +- src/modes/cmode_l.cpp | 98 +- src/modes/cmode_m.cpp | 37 +- src/modes/cmode_n.cpp | 37 +- src/modes/cmode_o.cpp | 154 +- src/modes/cmode_p.cpp | 36 +- src/modes/cmode_s.cpp | 36 +- src/modes/cmode_t.cpp | 37 +- src/modes/cmode_v.cpp | 153 +- src/modes/umode_i.cpp | 46 +- src/modes/umode_n.cpp | 59 +- src/modes/umode_o.cpp | 50 +- src/modes/umode_s.cpp | 46 +- src/modes/umode_w.cpp | 47 +- src/modules.cpp | 733 +-------- src/modules/extra/README | 8 +- src/modules/extra/m_filter_pcre.cpp | 183 +-- src/modules/extra/m_httpclienttest.cpp | 82 +- src/modules/extra/m_mysql.cpp | 890 +---------- src/modules/extra/m_pgsql.cpp | 985 +----------- src/modules/extra/m_sqlauth.cpp | 195 +-- src/modules/extra/m_sqlite3.cpp | 661 +------- src/modules/extra/m_sqllog.cpp | 311 +--- src/modules/extra/m_sqloper.cpp | 284 +--- src/modules/extra/m_sqlutils.cpp | 239 +-- src/modules/extra/m_sqlutils.h | 144 +- src/modules/extra/m_sqlv2.h | 606 +------- src/modules/extra/m_ssl_gnutls.cpp | 844 +---------- src/modules/extra/m_ssl_openssl.cpp | 902 +---------- src/modules/extra/m_ssl_oper_cert.cpp | 181 +-- src/modules/extra/m_sslinfo.cpp | 95 +- src/modules/extra/m_testclient.cpp | 111 +- src/modules/extra/m_ziplink.cpp | 453 +----- src/modules/httpclient.h | 128 +- src/modules/httpd.h | 167 +- src/modules/m_alias.cpp | 273 +--- src/modules/m_alltime.cpp | 84 +- src/modules/m_antibear.cpp | 79 +- src/modules/m_antibottler.cpp | 100 +- src/modules/m_auditorium.cpp | 192 +-- src/modules/m_banexception.cpp | 154 +- src/modules/m_banredirect.cpp | 344 +---- src/modules/m_blockamsg.cpp | 192 +-- src/modules/m_blockcaps.cpp | 144 +- src/modules/m_blockcolor.cpp | 119 +- src/modules/m_botmode.cpp | 96 +- src/modules/m_cban.cpp | 252 +--- src/modules/m_censor.cpp | 197 +-- src/modules/m_cgiirc.cpp | 512 +------ src/modules/m_chancreate.cpp | 56 +- src/modules/m_chanfilter.cpp | 156 +- src/modules/m_chanprotect.cpp | 532 +------ src/modules/m_check.cpp | 189 +-- src/modules/m_chghost.cpp | 121 +- src/modules/m_chgident.cpp | 93 +- src/modules/m_chgname.cpp | 90 +- src/modules/m_cloaking.cpp | 316 +--- src/modules/m_clones.cpp | 101 +- src/modules/m_conn_join.cpp | 97 +- src/modules/m_conn_umodes.cpp | 105 +- src/modules/m_conn_waitpong.cpp | 149 +- src/modules/m_connflood.cpp | 121 +- src/modules/m_cycle.cpp | 100 +- src/modules/m_dccallow.cpp | 490 +----- src/modules/m_deaf.cpp | 136 +- src/modules/m_denychans.cpp | 81 +- src/modules/m_devoice.cpp | 82 +- src/modules/m_dnsbl.cpp | 354 +---- src/modules/m_filter.cpp | 136 +- src/modules/m_filter.h | 454 +----- src/modules/m_foobar.cpp | 99 +- src/modules/m_globalload.cpp | 142 +- src/modules/m_globops.cpp | 77 +- src/modules/m_hash.h | 197 +-- src/modules/m_helpop.cpp | 192 +-- src/modules/m_hidechans.cpp | 96 +- src/modules/m_hideoper.cpp | 95 +- src/modules/m_hostchange.cpp | 149 +- src/modules/m_http_client.cpp | 347 +---- src/modules/m_httpd.cpp | 420 +----- src/modules/m_httpd_stats.cpp | 242 +-- src/modules/m_ident.cpp | 327 +--- src/modules/m_invisible.cpp | 278 +--- src/modules/m_inviteexception.cpp | 151 +- src/modules/m_joinflood.cpp | 286 +--- src/modules/m_jumpserver.cpp | 165 +- src/modules/m_kicknorejoin.cpp | 225 +-- src/modules/m_knock.cpp | 130 +- src/modules/m_lockserv.cpp | 132 +- src/modules/m_md5.cpp | 323 +--- src/modules/m_messageflood.cpp | 305 +--- src/modules/m_namesx.cpp | 128 +- src/modules/m_nicklock.cpp | 160 +- src/modules/m_noctcp.cpp | 108 +- src/modules/m_noinvite.cpp | 89 +- src/modules/m_nokicks.cpp | 106 +- src/modules/m_nonicks.cpp | 103 +- src/modules/m_nonotice.cpp | 104 +- src/modules/m_oper_hash.cpp | 164 +- src/modules/m_operchans.cpp | 98 +- src/modules/m_operjoin.cpp | 91 +- src/modules/m_operlevels.cpp | 123 +- src/modules/m_operlog.cpp | 76 +- src/modules/m_opermodes.cpp | 110 +- src/modules/m_opermotd.cpp | 117 +- src/modules/m_override.cpp | 295 +--- src/modules/m_randquote.cpp | 139 +- src/modules/m_redirect.cpp | 161 +- src/modules/m_regonlycreate.cpp | 62 +- src/modules/m_remove.cpp | 289 +--- src/modules/m_restrictbanned.cpp | 99 +- src/modules/m_restrictchans.cpp | 86 +- src/modules/m_restrictmsg.cpp | 76 +- src/modules/m_safelist.cpp | 269 +--- src/modules/m_sajoin.cpp | 115 +- src/modules/m_samode.cpp | 99 +- src/modules/m_sanick.cpp | 98 +- src/modules/m_sapart.cpp | 114 +- src/modules/m_saquit.cpp | 83 +- src/modules/m_securelist.cpp | 98 +- src/modules/m_seenicks.cpp | 56 +- src/modules/m_services.cpp | 311 +--- src/modules/m_services_account.cpp | 333 +--- src/modules/m_sethost.cpp | 109 +- src/modules/m_setident.cpp | 84 +- src/modules/m_setidle.cpp | 75 +- src/modules/m_setname.cpp | 81 +- src/modules/m_sha256.cpp | 297 +--- src/modules/m_showwhois.cpp | 110 +- src/modules/m_silence.cpp | 216 +-- src/modules/m_silence_ext.cpp | 373 +---- src/modules/m_spanningtree/README | 25 +- src/modules/m_spanningtree/handshaketimer.cpp | 63 +- src/modules/m_spanningtree/handshaketimer.h | 38 +- src/modules/m_spanningtree/link.h | 43 +- src/modules/m_spanningtree/main.cpp | 1393 +---------------- src/modules/m_spanningtree/main.h | 199 +-- src/modules/m_spanningtree/rconnect.cpp | 68 +- src/modules/m_spanningtree/rconnect.h | 29 +- src/modules/m_spanningtree/resolvers.cpp | 89 +- src/modules/m_spanningtree/resolvers.h | 91 +- src/modules/m_spanningtree/rsquit.cpp | 124 +- src/modules/m_spanningtree/rsquit.h | 30 +- src/modules/m_spanningtree/timesynctimer.cpp | 53 +- src/modules/m_spanningtree/timesynctimer.h | 48 +- src/modules/m_spanningtree/treeserver.cpp | 326 +--- src/modules/m_spanningtree/treeserver.h | 187 +-- src/modules/m_spanningtree/treesocket.h | 414 +---- src/modules/m_spanningtree/treesocket1.cpp | 1274 +--------------- src/modules/m_spanningtree/treesocket2.cpp | 1555 +------------------ src/modules/m_spanningtree/utils.cpp | 650 +------- src/modules/m_spanningtree/utils.h | 195 +-- src/modules/m_spy.cpp | 164 +- src/modules/m_ssl_dummy.cpp | 85 +- src/modules/m_sslmodes.cpp | 146 +- src/modules/m_stripcolor.cpp | 186 +-- src/modules/m_svshold.cpp | 283 +--- src/modules/m_swhois.cpp | 268 +--- src/modules/m_taxonomy.cpp | 100 +- src/modules/m_testcommand.cpp | 68 +- src/modules/m_timedbans.cpp | 205 +-- src/modules/m_tline.cpp | 96 +- src/modules/m_uhnames.cpp | 99 +- src/modules/m_uninvite.cpp | 108 +- src/modules/m_userip.cpp | 87 +- src/modules/m_vhost.cpp | 96 +- src/modules/m_watch.cpp | 473 +----- src/modules/m_xmlsocket.cpp | 171 +-- src/modules/transport.h | 232 +-- src/snomasks.cpp | 103 +- src/socket.cpp | 569 +------ src/socketengine.cpp | 94 +- src/socketengine_epoll.cpp | 158 +- src/socketengine_iocp.cpp | 377 +---- src/socketengine_kqueue.cpp | 159 +- src/socketengine_ports.cpp | 130 +- src/socketengine_select.cpp | 168 +-- src/timer.cpp | 136 +- src/userprocess.cpp | 306 +--- src/users.cpp | 2008 +------------------------ src/version.sh | 3 +- src/wildcard.cpp | 149 +- src/xline.cpp | 898 +---------- 256 files changed, 256 insertions(+), 54879 deletions(-) (limited to 'src') diff --git a/src/base.cpp b/src/base.cpp index 9c002773b..ef6ff5ccf 100644 --- a/src/base.cpp +++ b/src/base.cpp @@ -1,95 +1 @@ -/* +------------------------------------+ - * | Inspire Internet Relay Chat Daemon | - * +------------------------------------+ - * - * InspIRCd: (C) 2002-2007 InspIRCd Development Team - * 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 -#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 &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; -} +/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * 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 #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 &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 diff --git a/src/channels.cpp b/src/channels.cpp index b44a863b4..74543925e 100644 --- a/src/channels.cpp +++ b/src/channels.cpp @@ -1,1067 +1 @@ -/* +------------------------------------+ - * | Inspire Internet Relay Chat Daemon | - * +------------------------------------+ - * - * InspIRCd: (C) 2002-2007 InspIRCd Development Team - * See: http://www.inspircd.org/wiki/index.php/Credits - * - * This program is free but copyrighted software; see - * the file COPYING for details. - * - * --------------------------------------------------- - */ - -#include "inspircd.h" -#include -#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 : ""); - 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::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::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(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); - } -} - +/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include #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 : ""); 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::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::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(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 diff --git a/src/cmd_admin.cpp b/src/cmd_admin.cpp index 698468d0c..acfb8da43 100644 --- a/src/cmd_admin.cpp +++ b/src/cmd_admin.cpp @@ -1,35 +1 @@ -/* +------------------------------------+ - * | Inspire Internet Relay Chat Daemon | - * +------------------------------------+ - * - * InspIRCd: (C) 2002-2007 InspIRCd Development Team - * See: http://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; -} +/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://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 diff --git a/src/cmd_away.cpp b/src/cmd_away.cpp index 10f96c80b..894924938 100644 --- a/src/cmd_away.cpp +++ b/src/cmd_away.cpp @@ -1,42 +1 @@ -/* +------------------------------------+ - * | Inspire Internet Relay Chat Daemon | - * +------------------------------------+ - * - * InspIRCd: (C) 2002-2007 InspIRCd Development Team - * See: http://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; -} +/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://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 diff --git a/src/cmd_clearcache.cpp b/src/cmd_clearcache.cpp index daf8fa78b..cdc4b987c 100644 --- a/src/cmd_clearcache.cpp +++ b/src/cmd_clearcache.cpp @@ -1,31 +1 @@ -/* +------------------------------------+ - * | Inspire Internet Relay Chat Daemon | - * +------------------------------------+ - * - * InspIRCd: (C) 2002-2007 InspIRCd Development Team - * See: http://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; -} +/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://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 diff --git a/src/cmd_commands.cpp b/src/cmd_commands.cpp index fc95de5f1..9eff6ed50 100644 --- a/src/cmd_commands.cpp +++ b/src/cmd_commands.cpp @@ -1,33 +1 @@ -/* +------------------------------------+ - * | Inspire Internet Relay Chat Daemon | - * +------------------------------------+ - * - * InspIRCd: (C) 2002-2007 InspIRCd Development Team - * See: http://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; -} +/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://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 diff --git a/src/cmd_connect.cpp b/src/cmd_connect.cpp index fc3ae9d7a..da4a11565 100644 --- a/src/cmd_connect.cpp +++ b/src/cmd_connect.cpp @@ -1,33 +1 @@ -/* +------------------------------------+ - * | Inspire Internet Relay Chat Daemon | - * +------------------------------------+ - * - * InspIRCd: (C) 2002-2007 InspIRCd Development Team - * See: http://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; -} +/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://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 diff --git a/src/cmd_die.cpp b/src/cmd_die.cpp index 7f0a81df8..c8195a41c 100644 --- a/src/cmd_die.cpp +++ b/src/cmd_die.cpp @@ -1,47 +1 @@ -/* +------------------------------------+ - * | Inspire Internet Relay Chat Daemon | - * +------------------------------------+ - * - * InspIRCd: (C) 2002-2007 InspIRCd Development Team - * See: http://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; -} +/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://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 diff --git a/src/cmd_eline.cpp b/src/cmd_eline.cpp index 793f7a413..0282369c1 100644 --- a/src/cmd_eline.cpp +++ b/src/cmd_eline.cpp @@ -1,77 +1 @@ -/* +------------------------------------+ - * | Inspire Internet Relay Chat Daemon | - * +------------------------------------+ - * - * InspIRCd: (C) 2002-2007 InspIRCd Development Team - * See: http://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; -} +/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://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 diff --git a/src/cmd_gline.cpp b/src/cmd_gline.cpp index f208d40bf..c1632df82 100644 --- a/src/cmd_gline.cpp +++ b/src/cmd_gline.cpp @@ -1,89 +1 @@ -/* +------------------------------------+ - * | Inspire Internet Relay Chat Daemon | - * +------------------------------------+ - * - * InspIRCd: (C) 2002-2007 InspIRCd Development Team - * See: http://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; -} - +/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://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 diff --git a/src/cmd_info.cpp b/src/cmd_info.cpp index 3564b0c6a..de6c18d82 100644 --- a/src/cmd_info.cpp +++ b/src/cmd_info.cpp @@ -1,75 +1 @@ -/* +------------------------------------+ - * | Inspire Internet Relay Chat Daemon | - * +------------------------------------+ - * - * InspIRCd: (C) 2002-2007 InspIRCd Development Team - * See: http://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; -} +/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://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 diff --git a/src/cmd_invite.cpp b/src/cmd_invite.cpp index 0eb3a2354..19330783e 100644 --- a/src/cmd_invite.cpp +++ b/src/cmd_invite.cpp @@ -1,98 +1 @@ -/* +------------------------------------+ - * | Inspire Internet Relay Chat Daemon | - * +------------------------------------+ - * - * InspIRCd: (C) 2002-2007 InspIRCd Development Team - * See: http://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; -} - +/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://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 diff --git a/src/cmd_ison.cpp b/src/cmd_ison.cpp index 0cee160c7..f63019427 100644 --- a/src/cmd_ison.cpp +++ b/src/cmd_ison.cpp @@ -1,86 +1 @@ -/* +------------------------------------+ - * | Inspire Internet Relay Chat Daemon | - * +------------------------------------+ - * - * InspIRCd: (C) 2002-2007 InspIRCd Development Team - * See: http://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 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; -} - +/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://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 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 diff --git a/src/cmd_join.cpp b/src/cmd_join.cpp index 218e0f7b8..6780ac4be 100644 --- a/src/cmd_join.cpp +++ b/src/cmd_join.cpp @@ -1,52 +1 @@ -/* +------------------------------------+ - * | Inspire Internet Relay Chat Daemon | - * +------------------------------------+ - * - * InspIRCd: (C) 2002-2007 InspIRCd Development Team - * See: http://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; -} +/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://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 diff --git a/src/cmd_kick.cpp b/src/cmd_kick.cpp index 17933a43f..263cf35c5 100644 --- a/src/cmd_kick.cpp +++ b/src/cmd_kick.cpp @@ -1,58 +1 @@ -/* +------------------------------------+ - * | Inspire Internet Relay Chat Daemon | - * +------------------------------------+ - * - * InspIRCd: (C) 2002-2007 InspIRCd Development Team - * See: http://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; -} +/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://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 diff --git a/src/cmd_kill.cpp b/src/cmd_kill.cpp index 26f354b8f..b8dbfc345 100644 --- a/src/cmd_kill.cpp +++ b/src/cmd_kill.cpp @@ -1,117 +1 @@ -/* +------------------------------------+ - * | Inspire Internet Relay Chat Daemon | - * +------------------------------------+ - * - * InspIRCd: (C) 2002-2007 InspIRCd Development Team - * See: http://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; -} - +/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://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 diff --git a/src/cmd_kline.cpp b/src/cmd_kline.cpp index 05856893a..ac1aafe15 100644 --- a/src/cmd_kline.cpp +++ b/src/cmd_kline.cpp @@ -1,88 +1 @@ -/* +------------------------------------+ - * | Inspire Internet Relay Chat Daemon | - * +------------------------------------+ - * - * InspIRCd: (C) 2002-2007 InspIRCd Development Team - * See: http://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; -} - +/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://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 diff --git a/src/cmd_links.cpp b/src/cmd_links.cpp index 83e558d5d..39f01383e 100644 --- a/src/cmd_links.cpp +++ b/src/cmd_links.cpp @@ -1,32 +1 @@ -/* +------------------------------------+ - * | Inspire Internet Relay Chat Daemon | - * +------------------------------------+ - * - * InspIRCd: (C) 2002-2007 InspIRCd Development Team - * See: http://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; -} +/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://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 diff --git a/src/cmd_list.cpp b/src/cmd_list.cpp index ed956e7f1..14cf45272 100644 --- a/src/cmd_list.cpp +++ b/src/cmd_list.cpp @@ -1,85 +1 @@ -/* +------------------------------------+ - * | Inspire Internet Relay Chat Daemon | - * +------------------------------------+ - * - * InspIRCd: (C) 2002-2007 InspIRCd Development Team - * See: http://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; -} +/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://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 diff --git a/src/cmd_loadmodule.cpp b/src/cmd_loadmodule.cpp index 08179d120..07776c1a8 100644 --- a/src/cmd_loadmodule.cpp +++ b/src/cmd_loadmodule.cpp @@ -1,39 +1 @@ -/* +------------------------------------+ - * | Inspire Internet Relay Chat Daemon | - * +------------------------------------+ - * - * InspIRCd: (C) 2002-2007 InspIRCd Development Team - * See: http://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; - } -} - +/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://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 diff --git a/src/cmd_lusers.cpp b/src/cmd_lusers.cpp index fa58ca076..87194d9e2 100644 --- a/src/cmd_lusers.cpp +++ b/src/cmd_lusers.cpp @@ -1,42 +1 @@ -/* +------------------------------------+ - * | Inspire Internet Relay Chat Daemon | - * +------------------------------------+ - * - * InspIRCd: (C) 2002-2007 InspIRCd Development Team - * See: http://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; -} - +/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://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 diff --git a/src/cmd_map.cpp b/src/cmd_map.cpp index c75a18b3f..270374cbf 100644 --- a/src/cmd_map.cpp +++ b/src/cmd_map.cpp @@ -1,35 +1 @@ -/* +------------------------------------+ - * | Inspire Internet Relay Chat Daemon | - * +------------------------------------+ - * - * InspIRCd: (C) 2002-2007 InspIRCd Development Team - * See: http://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; -} +/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://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 diff --git a/src/cmd_mode.cpp b/src/cmd_mode.cpp index 18c1e69ad..563fa75db 100644 --- a/src/cmd_mode.cpp +++ b/src/cmd_mode.cpp @@ -1,31 +1 @@ -/* +------------------------------------+ - * | Inspire Internet Relay Chat Daemon | - * +------------------------------------+ - * - * InspIRCd: (C) 2002-2007 InspIRCd Development Team - * See: http://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; -} - +/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://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 diff --git a/src/cmd_modules.cpp b/src/cmd_modules.cpp index be236fcd9..b8812c22d 100644 --- a/src/cmd_modules.cpp +++ b/src/cmd_modules.cpp @@ -1,75 +1 @@ -/* +------------------------------------+ - * | Inspire Internet Relay Chat Daemon | - * +------------------------------------+ - * - * InspIRCd: (C) 2002-2007 InspIRCd Development Team - * See: http://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," "); - 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; -} +/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://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," "); 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 diff --git a/src/cmd_motd.cpp b/src/cmd_motd.cpp index 6aaf1a9af..f14af0bd1 100644 --- a/src/cmd_motd.cpp +++ b/src/cmd_motd.cpp @@ -1,29 +1 @@ -/* +------------------------------------+ - * | Inspire Internet Relay Chat Daemon | - * +------------------------------------+ - * - * InspIRCd: (C) 2002-2007 InspIRCd Development Team - * See: http://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; -} +/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://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 diff --git a/src/cmd_names.cpp b/src/cmd_names.cpp index fe61c4dea..c157c3c04 100644 --- a/src/cmd_names.cpp +++ b/src/cmd_names.cpp @@ -1,54 +1 @@ -/* +------------------------------------+ - * | Inspire Internet Relay Chat Daemon | - * +------------------------------------+ - * - * InspIRCd: (C) 2002-2007 InspIRCd Development Team - * See: http://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; -} +/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://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 diff --git a/src/cmd_nick.cpp b/src/cmd_nick.cpp index 4e2ebcf6e..5bc9ce6d1 100644 --- a/src/cmd_nick.cpp +++ b/src/cmd_nick.cpp @@ -1,189 +1 @@ -/* +------------------------------------+ - * | Inspire Internet Relay Chat Daemon | - * +------------------------------------+ - * - * InspIRCd: (C) 2002-2007 InspIRCd Development Team - * See: http://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; - -} - +/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://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 diff --git a/src/cmd_notice.cpp b/src/cmd_notice.cpp index a797fefab..27fdd5279 100644 --- a/src/cmd_notice.cpp +++ b/src/cmd_notice.cpp @@ -1,158 +1 @@ -/* +------------------------------------+ - * | Inspire Internet Relay Chat Daemon | - * +------------------------------------+ - * - * InspIRCd: (C) 2002-2007 InspIRCd Development Team - * See: http://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; - -} - +/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://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 diff --git a/src/cmd_oper.cpp b/src/cmd_oper.cpp index 686182876..af811115d 100644 --- a/src/cmd_oper.cpp +++ b/src/cmd_oper.cpp @@ -1,153 +1 @@ -/* +------------------------------------+ - * | Inspire Internet Relay Chat Daemon | - * +------------------------------------+ - * - * InspIRCd: (C) 2002-2007 InspIRCd Development Team - * See: http://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 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; -} - +/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://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 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 diff --git a/src/cmd_part.cpp b/src/cmd_part.cpp index 347466c22..3bf22ac5d 100644 --- a/src/cmd_part.cpp +++ b/src/cmd_part.cpp @@ -1,43 +1 @@ -/* +------------------------------------+ - * | Inspire Internet Relay Chat Daemon | - * +------------------------------------+ - * - * InspIRCd: (C) 2002-2007 InspIRCd Development Team - * See: http://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; -} +/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://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 diff --git a/src/cmd_pass.cpp b/src/cmd_pass.cpp index 29c83c3bf..0a836ccdf 100644 --- a/src/cmd_pass.cpp +++ b/src/cmd_pass.cpp @@ -1,42 +1 @@ -/* +------------------------------------+ - * | Inspire Internet Relay Chat Daemon | - * +------------------------------------+ - * - * InspIRCd: (C) 2002-2007 InspIRCd Development Team - * See: http://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; -} +/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://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 diff --git a/src/cmd_ping.cpp b/src/cmd_ping.cpp index afb708d70..c36415d12 100644 --- a/src/cmd_ping.cpp +++ b/src/cmd_ping.cpp @@ -1,28 +1 @@ -/* +------------------------------------+ - * | Inspire Internet Relay Chat Daemon | - * +------------------------------------+ - * - * InspIRCd: (C) 2002-2007 InspIRCd Development Team - * See: http://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; -} +/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://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 diff --git a/src/cmd_pong.cpp b/src/cmd_pong.cpp index c89542240..16b42355b 100644 --- a/src/cmd_pong.cpp +++ b/src/cmd_pong.cpp @@ -1,28 +1 @@ -/* +------------------------------------+ - * | Inspire Internet Relay Chat Daemon | - * +------------------------------------+ - * - * InspIRCd: (C) 2002-2007 InspIRCd Development Team - * See: http://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; -} +/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://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 diff --git a/src/cmd_privmsg.cpp b/src/cmd_privmsg.cpp index f3295df07..3fdc12090 100644 --- a/src/cmd_privmsg.cpp +++ b/src/cmd_privmsg.cpp @@ -1,164 +1 @@ -/* +------------------------------------+ - * | Inspire Internet Relay Chat Daemon | - * +------------------------------------+ - * - * InspIRCd: (C) 2002-2007 InspIRCd Development Team - * See: http://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; -} - +/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://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 diff --git a/src/cmd_qline.cpp b/src/cmd_qline.cpp index bb1122854..892f6480c 100644 --- a/src/cmd_qline.cpp +++ b/src/cmd_qline.cpp @@ -1,80 +1 @@ -/* +------------------------------------+ - * | Inspire Internet Relay Chat Daemon | - * +------------------------------------+ - * - * InspIRCd: (C) 2002-2007 InspIRCd Development Team - * See: http://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; -} - +/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://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 diff --git a/src/cmd_quit.cpp b/src/cmd_quit.cpp index a859c1790..45e970207 100644 --- a/src/cmd_quit.cpp +++ b/src/cmd_quit.cpp @@ -1,48 +1 @@ -/* +------------------------------------+ - * | Inspire Internet Relay Chat Daemon | - * +------------------------------------+ - * - * InspIRCd: (C) 2002-2007 InspIRCd Development Team - * See: http://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; -} - +/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://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 diff --git a/src/cmd_rehash.cpp b/src/cmd_rehash.cpp index 34789b0ea..c8415f313 100644 --- a/src/cmd_rehash.cpp +++ b/src/cmd_rehash.cpp @@ -1,56 +1 @@ -/* +------------------------------------+ - * | Inspire Internet Relay Chat Daemon | - * +------------------------------------+ - * - * InspIRCd: (C) 2002-2007 InspIRCd Development Team - * See: http://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; -} - +/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://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 diff --git a/src/cmd_reloadmodule.cpp b/src/cmd_reloadmodule.cpp index 41da0c40d..0eb6f1ec4 100644 --- a/src/cmd_reloadmodule.cpp +++ b/src/cmd_reloadmodule.cpp @@ -1,39 +1 @@ -/* +------------------------------------+ - * | Inspire Internet Relay Chat Daemon | - * +------------------------------------+ - * - * InspIRCd: (C) 2002-2007 InspIRCd Development Team - * See: http://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; -} +/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://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 diff --git a/src/cmd_restart.cpp b/src/cmd_restart.cpp index c910df8ba..1dbf5b121 100644 --- a/src/cmd_restart.cpp +++ b/src/cmd_restart.cpp @@ -1,49 +1 @@ -/* +------------------------------------+ - * | Inspire Internet Relay Chat Daemon | - * +------------------------------------+ - * - * InspIRCd: (C) 2002-2007 InspIRCd Development Team - * See: http://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; -} - +/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://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 diff --git a/src/cmd_rules.cpp b/src/cmd_rules.cpp index 95b744412..ef531732d 100644 --- a/src/cmd_rules.cpp +++ b/src/cmd_rules.cpp @@ -1,27 +1 @@ -/* +------------------------------------+ - * | Inspire Internet Relay Chat Daemon | - * +------------------------------------+ - * - * InspIRCd: (C) 2002-2007 InspIRCd Development Team - * See: http://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; -} +/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://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 diff --git a/src/cmd_server.cpp b/src/cmd_server.cpp index cace13c38..acad55b1b 100644 --- a/src/cmd_server.cpp +++ b/src/cmd_server.cpp @@ -1,30 +1 @@ -/* +------------------------------------+ - * | Inspire Internet Relay Chat Daemon | - * +------------------------------------+ - * - * InspIRCd: (C) 2002-2007 InspIRCd Development Team - * See: http://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; -} +/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://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 diff --git a/src/cmd_squit.cpp b/src/cmd_squit.cpp index 57105109b..d6fe63c94 100644 --- a/src/cmd_squit.cpp +++ b/src/cmd_squit.cpp @@ -1,32 +1 @@ -/* +------------------------------------+ - * | Inspire Internet Relay Chat Daemon | - * +------------------------------------+ - * - * InspIRCd: (C) 2002-2007 InspIRCd Development Team - * See: http://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; -} +/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://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 diff --git a/src/cmd_stats.cpp b/src/cmd_stats.cpp index 98b2c63dd..4bf872981 100644 --- a/src/cmd_stats.cpp +++ b/src/cmd_stats.cpp @@ -1,318 +1 @@ -/* +------------------------------------+ - * | Inspire Internet Relay Chat Daemon | - * +------------------------------------+ - * - * InspIRCd: (C) 2002-2007 InspIRCd Development Team - * See: http://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 - -/* 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 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::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::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; -} - +/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://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 /* 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 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::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::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 diff --git a/src/cmd_summon.cpp b/src/cmd_summon.cpp index 520bdf090..134ac9f2f 100644 --- a/src/cmd_summon.cpp +++ b/src/cmd_summon.cpp @@ -1,27 +1 @@ -/* +------------------------------------+ - * | Inspire Internet Relay Chat Daemon | - * +------------------------------------+ - * - * InspIRCd: (C) 2002-2007 InspIRCd Development Team - * See: http://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; -} +/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://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 diff --git a/src/cmd_time.cpp b/src/cmd_time.cpp index cd0f0e1c7..272a0ce82 100644 --- a/src/cmd_time.cpp +++ b/src/cmd_time.cpp @@ -1,40 +1 @@ -/* +------------------------------------+ - * | Inspire Internet Relay Chat Daemon | - * +------------------------------------+ - * - * InspIRCd: (C) 2002-2007 InspIRCd Development Team - * See: http://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; -} +/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://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 diff --git a/src/cmd_topic.cpp b/src/cmd_topic.cpp index 741558282..82f21b24e 100644 --- a/src/cmd_topic.cpp +++ b/src/cmd_topic.cpp @@ -1,118 +1 @@ -/* +------------------------------------+ - * | Inspire Internet Relay Chat Daemon | - * +------------------------------------+ - * - * InspIRCd: (C) 2002-2007 InspIRCd Development Team - * See: http://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; -} - +/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://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 diff --git a/src/cmd_trace.cpp b/src/cmd_trace.cpp index 42105df98..9cb58e8ee 100644 --- a/src/cmd_trace.cpp +++ b/src/cmd_trace.cpp @@ -1,46 +1 @@ -/* +------------------------------------+ - * | Inspire Internet Relay Chat Daemon | - * +------------------------------------+ - * - * InspIRCd: (C) 2002-2007 InspIRCd Development Team - * See: http://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; -} +/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://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 diff --git a/src/cmd_unloadmodule.cpp b/src/cmd_unloadmodule.cpp index 44c2133e7..51192aa7a 100644 --- a/src/cmd_unloadmodule.cpp +++ b/src/cmd_unloadmodule.cpp @@ -1,39 +1 @@ -/* +------------------------------------+ - * | Inspire Internet Relay Chat Daemon | - * +------------------------------------+ - * - * InspIRCd: (C) 2002-2007 InspIRCd Development Team - * See: http://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; -} +/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://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 diff --git a/src/cmd_user.cpp b/src/cmd_user.cpp index dc224db76..aa9e3d321 100644 --- a/src/cmd_user.cpp +++ b/src/cmd_user.cpp @@ -1,69 +1 @@ -/* +------------------------------------+ - * | Inspire Internet Relay Chat Daemon | - * +------------------------------------+ - * - * InspIRCd: (C) 2002-2007 InspIRCd Development Team - * See: http://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; -} +/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://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 diff --git a/src/cmd_userhost.cpp b/src/cmd_userhost.cpp index 9e644bdd1..c0a2362cd 100644 --- a/src/cmd_userhost.cpp +++ b/src/cmd_userhost.cpp @@ -1,63 +1 @@ -/* +------------------------------------+ - * | Inspire Internet Relay Chat Daemon | - * +------------------------------------+ - * - * InspIRCd: (C) 2002-2007 InspIRCd Development Team - * See: http://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; -} +/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://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 diff --git a/src/cmd_users.cpp b/src/cmd_users.cpp index 97b9b247d..c3cb02075 100644 --- a/src/cmd_users.cpp +++ b/src/cmd_users.cpp @@ -1,27 +1 @@ -/* +------------------------------------+ - * | Inspire Internet Relay Chat Daemon | - * +------------------------------------+ - * - * InspIRCd: (C) 2002-2007 InspIRCd Development Team - * See: http://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; -} +/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://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 diff --git a/src/cmd_version.cpp b/src/cmd_version.cpp index 599b7bf75..a33dd5bd0 100644 --- a/src/cmd_version.cpp +++ b/src/cmd_version.cpp @@ -1,31 +1 @@ -/* +------------------------------------+ - * | Inspire Internet Relay Chat Daemon | - * +------------------------------------+ - * - * InspIRCd: (C) 2002-2007 InspIRCd Development Team - * See: http://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; -} +/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://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 diff --git a/src/cmd_wallops.cpp b/src/cmd_wallops.cpp index d32b19ebd..88c8fccf5 100644 --- a/src/cmd_wallops.cpp +++ b/src/cmd_wallops.cpp @@ -1,31 +1 @@ -/* +------------------------------------+ - * | Inspire Internet Relay Chat Daemon | - * +------------------------------------+ - * - * InspIRCd: (C) 2002-2007 InspIRCd Development Team - * See: http://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; -} +/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://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 diff --git a/src/cmd_who.cpp b/src/cmd_who.cpp index 31e8030f5..6054351d9 100644 --- a/src/cmd_who.cpp +++ b/src/cmd_who.cpp @@ -1,328 +1 @@ -/* +------------------------------------+ - * | Inspire Internet Relay Chat Daemon | - * +------------------------------------+ - * - * InspIRCd: (C) 2002-2007 InspIRCd Development Team - * See: http://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 &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 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 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::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::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; - } -} +/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://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 &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 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 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::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::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 diff --git a/src/cmd_whois.cpp b/src/cmd_whois.cpp index 897ec60ac..3797efeaa 100644 --- a/src/cmd_whois.cpp +++ b/src/cmd_whois.cpp @@ -1,144 +1 @@ -/* +------------------------------------+ - * | Inspire Internet Relay Chat Daemon | - * +------------------------------------+ - * - * InspIRCd: (C) 2002-2007 InspIRCd Development Team - * See: http://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; -} - +/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://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 diff --git a/src/cmd_whowas.cpp b/src/cmd_whowas.cpp index 2d504c47c..aab457243 100644 --- a/src/cmd_whowas.cpp +++ b/src/cmd_whowas.cpp @@ -1,341 +1 @@ -/* +------------------------------------+ - * | Inspire Internet Relay Chat Daemon | - * +------------------------------------+ - * - * InspIRCd: (C) 2002-2007 InspIRCd Development Team - * See: http://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 = "{,}"; - 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 ¶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 params; - whowas_command->HandleInternal(WHOWAS_MAINTAIN, params); - } -} +/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://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 = "{,}"; 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 ¶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 params; whowas_command->HandleInternal(WHOWAS_MAINTAIN, params); } } \ No newline at end of file diff --git a/src/cmd_zline.cpp b/src/cmd_zline.cpp index 8fea70656..43a0fd042 100644 --- a/src/cmd_zline.cpp +++ b/src/cmd_zline.cpp @@ -1,80 +1 @@ -/* +------------------------------------+ - * | Inspire Internet Relay Chat Daemon | - * +------------------------------------+ - * - * InspIRCd: (C) 2002-2007 InspIRCd Development Team - * See: http://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; -} +/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://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 diff --git a/src/command_parse.cpp b/src/command_parse.cpp index 52c58ef99..bf2e61ce9 100644 --- a/src/command_parse.cpp +++ b/src/command_parse.cpp @@ -1,563 +1 @@ -/* +------------------------------------+ - * | Inspire Internet Relay Chat Daemon | - * +------------------------------------+ - * - * InspIRCd: (C) 2002-2007 InspIRCd Development Team - * See: http://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 -#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 -#include -#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::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 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 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 != "")) - { - 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)); -} - +/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://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 #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 #include #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::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 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 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 != "")) { 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 diff --git a/src/commands.cpp b/src/commands.cpp index 65b11e5a0..469b4ed74 100644 --- a/src/commands.cpp +++ b/src/commands.cpp @@ -1,111 +1 @@ -/* +------------------------------------+ - * | Inspire Internet Relay Chat Daemon | - * +------------------------------------+ - * - * InspIRCd: (C) 2002-2007 InspIRCd Development Team - * See: http://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; -} +/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://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 diff --git a/src/configreader.cpp b/src/configreader.cpp index d4291692f..df15d9a61 100644 --- a/src/configreader.cpp +++ b/src/configreader.cpp @@ -1,1714 +1 @@ -/* +------------------------------------+ - * | Inspire Internet Relay Chat Daemon | - * +------------------------------------+ - * - * InspIRCd: (C) 2002-2007 InspIRCd Development Team - * See: http://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 -#include -#include "xline.h" -#include "exitcodes.h" -#include "commands/cmd_whowas.h" - -std::vector 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::iterator x = IOHookModule.find(port); - return (x != IOHookModule.end() ? x->second : NULL); -} - -Module* ServerConfig::GetIOHook(InspSocket* is) -{ - std::map::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::iterator x = IOHookModule.find(port); - if (x != IOHookModule.end()) - { - IOHookModule.erase(x); - return true; - } - return false; -} - -bool ServerConfig::DelIOHook(InspSocket* is) -{ - std::map::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::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: 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: 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: 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: 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," 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: 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," 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: '%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," 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: value less than 3600, setting to default 3600"); - } - - command_t* whowas_command = conf->GetInstance()->Parser->GetHandler("WHOWAS"); - if (whowas_command) - { - std::deque params; - whowas_command->HandleInternal(WHOWAS_PRUNE, params); - } - - return true; -} - -/* Callback called before processing the first 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 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 tags - */ -bool DoneConnect(ServerConfig* conf, const char* tag) -{ - return true; -} - -/* Callback called before processing the first tag - */ -bool InitULine(ServerConfig* conf, const char* tag) -{ - conf->ulines.clear(); - return true; -} - -/* Callback called to process a single 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 tags - */ -bool DoneULine(ServerConfig* conf, const char* tag) -{ - return true; -} - -/* Callback called before processing the first 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::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 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 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::iterator _new = new_module_names.begin(); _new != new_module_names.end(); _new++) - { - bool added = true; - - for (std::vector::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::iterator oldm = old_module_names.begin(); oldm != old_module_names.end(); oldm++) - { - bool removed = true; - for (std::vector::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 tag - */ -bool InitMaxBans(ServerConfig* conf, const char* tag) -{ - conf->maxbans.clear(); - return true; -} - -/* Callback called to process a single 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 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() ? "" : i->first.c_str(), (unsigned long)i->second); - } - } - - if (!removed_modules.empty()) - { - for (std::vector::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::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 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 (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"); -} - +/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://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 #include #include "xline.h" #include "exitcodes.h" #include "commands/cmd_whowas.h" std::vector 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::iterator x = IOHookModule.find(port); return (x != IOHookModule.end() ? x->second : NULL); } Module* ServerConfig::GetIOHook(InspSocket* is) { std::map::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::iterator x = IOHookModule.find(port); if (x != IOHookModule.end()) { IOHookModule.erase(x); return true; } return false; } bool ServerConfig::DelIOHook(InspSocket* is) { std::map::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::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: 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: 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: 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: 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," 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: 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," 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: '%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," 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: value less than 3600, setting to default 3600"); } command_t* whowas_command = conf->GetInstance()->Parser->GetHandler("WHOWAS"); if (whowas_command) { std::deque params; whowas_command->HandleInternal(WHOWAS_PRUNE, params); } return true; } /* Callback called before processing the first 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 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 tags */ bool DoneConnect(ServerConfig* conf, const char* tag) { return true; } /* Callback called before processing the first tag */ bool InitULine(ServerConfig* conf, const char* tag) { conf->ulines.clear(); return true; } /* Callback called to process a single 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 tags */ bool DoneULine(ServerConfig* conf, const char* tag) { return true; } /* Callback called before processing the first 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::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 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 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::iterator _new = new_module_names.begin(); _new != new_module_names.end(); _new++) { bool added = true; for (std::vector::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::iterator oldm = old_module_names.begin(); oldm != old_module_names.end(); oldm++) { bool removed = true; for (std::vector::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 tag */ bool InitMaxBans(ServerConfig* conf, const char* tag) { conf->maxbans.clear(); return true; } /* Callback called to process a single 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 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() ? "" : i->first.c_str(), (unsigned long)i->second); } } if (!removed_modules.empty()) { for (std::vector::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::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 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 (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 diff --git a/src/cull_list.cpp b/src/cull_list.cpp index ee257350e..049d2e6ba 100644 --- a/src/cull_list.cpp +++ b/src/cull_list.cpp @@ -1,202 +1 @@ -/* +------------------------------------+ - * | Inspire Internet Relay Chat Daemon | - * +------------------------------------+ - * - * InspIRCd: (C) 2002-2007 InspIRCd Development Team - * See: http://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::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::iterator a = list.begin(); - - user_hash::iterator iter = ServerInstance->clientlist->find(a->GetUser()->nick); - std::map::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::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; -} - +/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://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::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::iterator a = list.begin(); user_hash::iterator iter = ServerInstance->clientlist->find(a->GetUser()->nick); std::map::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::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 diff --git a/src/dns.cpp b/src/dns.cpp index fab9631b7..6e12662a7 100644 --- a/src/dns.cpp +++ b/src/dns.cpp @@ -1,1169 +1 @@ -/* +------------------------------------+ - * | Inspire Internet Relay Chat Daemon | - * +------------------------------------+ - * - * InspIRCd: (C) 2002-2007 InspIRCd 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 -#include -#include -#include -#include -#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 -} - +/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd 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 #include #include #include #include #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 diff --git a/src/dynamic.cpp b/src/dynamic.cpp index 179113cae..5d7759b47 100644 --- a/src/dynamic.cpp +++ b/src/dynamic.cpp @@ -1,89 +1 @@ -/* +------------------------------------+ - * | Inspire Internet Relay Chat Daemon | - * +------------------------------------+ - * - * InspIRCd: (C) 2002-2007 InspIRCd Development Team - * See: http://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 -#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() -{ -} - +/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://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 #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 diff --git a/src/hashcomp.cpp b/src/hashcomp.cpp index caf93ec1b..e5a6acffe 100644 --- a/src/hashcomp.cpp +++ b/src/hashcomp.cpp @@ -1,619 +1 @@ -/* +------------------------------------+ - * | Inspire Internet Relay Chat Daemon | - * +------------------------------------+ - * - * InspIRCd: (C) 2002-2007 InspIRCd Development Team - * See: http://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 -#define nspace __gnu_cxx -#else -#include -#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::operator()(const string &s) const -#else -size_t nspace::hash_compare >::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 - */ - 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::operator()(const irc::string &s) const -#else -size_t nspace::hash_compare >::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 &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 &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 &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; -} - +/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://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 #define nspace __gnu_cxx #else #include #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::operator()(const string &s) const #else size_t nspace::hash_compare >::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 */ 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::operator()(const irc::string &s) const #else size_t nspace::hash_compare >::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 &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 &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 &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 diff --git a/src/helperfuncs.cpp b/src/helperfuncs.cpp index da3fa5e1b..b5d8f5630 100644 --- a/src/helperfuncs.cpp +++ b/src/helperfuncs.cpp @@ -1,534 +1 @@ -/* +------------------------------------+ - * | Inspire Internet Relay Chat Daemon | - * +------------------------------------+ - * - * InspIRCd: (C) 2002-2007 InspIRCd Development Team - * See: http://www.inspircd.org/wiki/index.php/Credits - * - * This program is free but copyrighted software; see - * the file COPYING for details. - * - * --------------------------------------------------- - */ - -#include "inspircd.h" -#include -#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::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::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::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::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::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::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 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)); -} - +/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include #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::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::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::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::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::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::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 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 diff --git a/src/inspircd.cpp b/src/inspircd.cpp index 858862e9d..7f9342706 100644 --- a/src/inspircd.cpp +++ b/src/inspircd.cpp @@ -1,1307 +1 @@ -/* +------------------------------------+ - * | Inspire Internet Relay Chat Daemon | - * +------------------------------------+ - * - * InspIRCd: (C) 2002-2007 InspIRCd Development Team - * See: http://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 - -#ifndef WIN32 -#include -#include -#include -#include -#include - -/* Some systems don't define RUSAGE_SELF. This should fix them. */ -#ifndef RUSAGE_SELF - #define RUSAGE_SELF 0 -#endif - -#endif - -#include -#include -#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 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 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::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 ] [--runasroot] [--version] [--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() ? "" : 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::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::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 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 put_to_back; - std::vector put_to_front; - std::map put_before; - std::map 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::iterator j = put_before.begin(); j != put_before.end(); j++) - MoveBefore(j->first,j->second); - for (std::map::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::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(); -} - +/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://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 #ifndef WIN32 #include #include #include #include #include /* Some systems don't define RUSAGE_SELF. This should fix them. */ #ifndef RUSAGE_SELF #define RUSAGE_SELF 0 #endif #endif #include #include #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 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 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::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 ] [--runasroot] [--version] [--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() ? "" : 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::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::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 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 put_to_back; std::vector put_to_front; std::map put_before; std::map 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::iterator j = put_before.begin(); j != put_before.end(); j++) MoveBefore(j->first,j->second); for (std::map::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::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 diff --git a/src/inspsocket.cpp b/src/inspsocket.cpp index f29366c73..a2c5cc47f 100644 --- a/src/inspsocket.cpp +++ b/src/inspsocket.cpp @@ -1,750 +1 @@ -/* +------------------------------------+ - * | Inspire Internet Relay Chat Daemon | - * +------------------------------------+ - * - * InspIRCd: (C) 2002-2007 InspIRCd Development 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; - } -} - +/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development 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 diff --git a/src/inspstring.cpp b/src/inspstring.cpp index 98e7228d5..b5278a6ad 100644 --- a/src/inspstring.cpp +++ b/src/inspstring.cpp @@ -1,137 +1 @@ -/* +------------------------------------+ - * | Inspire Internet Relay Chat Daemon | - * +------------------------------------+ - * - * InspIRCd: (C) 2002-2007 InspIRCd Development 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 - * 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; -} - +/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development 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 * 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 diff --git a/src/mode.cpp b/src/mode.cpp index db120cfe5..cfb009273 100644 --- a/src/mode.cpp +++ b/src/mode.cpp @@ -1,1066 +1 @@ -/* +------------------------------------+ - * | Inspire Internet Relay Chat Daemon | - * +------------------------------------+ - * - * InspIRCd: (C) 2002-2007 InspIRCd Development Team - * See: http://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 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(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); -} +/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://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 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(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 diff --git a/src/modes/Makefile b/src/modes/Makefile index 8839168b7..1b7743649 100644 --- a/src/modes/Makefile +++ b/src/modes/Makefile @@ -1,65 +1 @@ -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 - +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 diff --git a/src/modes/cmode_b.cpp b/src/modes/cmode_b.cpp index e306f31f6..dbc2e925d 100644 --- a/src/modes/cmode_b.cpp +++ b/src/modes/cmode_b.cpp @@ -1,185 +1 @@ -/* +------------------------------------+ - * | Inspire Internet Relay Chat Daemon | - * +------------------------------------+ - * - * InspIRCd: (C) 2002-2007 InspIRCd Development Team - * See: http://www.inspircd.org/wiki/index.php/Credits - * - * This program is free but copyrighted software; see - * the file COPYING for details. - * - * --------------------------------------------------- - */ - -#include "inspircd.h" -#include -#include -#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; -} - +/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include #include #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 diff --git a/src/modes/cmode_h.cpp b/src/modes/cmode_h.cpp index ecee93388..f2ffff665 100644 --- a/src/modes/cmode_h.cpp +++ b/src/modes/cmode_h.cpp @@ -1,162 +1 @@ -/* +------------------------------------+ - * | Inspire Internet Relay Chat Daemon | - * +------------------------------------+ - * - * InspIRCd: (C) 2002-2007 InspIRCd Development Team - * See: http://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 ""; -} - +/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://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 diff --git a/src/modes/cmode_i.cpp b/src/modes/cmode_i.cpp index 12bdb18b8..1dd6dc857 100644 --- a/src/modes/cmode_i.cpp +++ b/src/modes/cmode_i.cpp @@ -1,35 +1 @@ -/* +------------------------------------+ - * | Inspire Internet Relay Chat Daemon | - * +------------------------------------+ - * - * InspIRCd: (C) 2002-2007 InspIRCd Development Team - * See: http://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; - } -} +/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://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 diff --git a/src/modes/cmode_k.cpp b/src/modes/cmode_k.cpp index eb59714f7..e06c35e83 100644 --- a/src/modes/cmode_k.cpp +++ b/src/modes/cmode_k.cpp @@ -1,103 +1 @@ -/* +------------------------------------+ - * | Inspire Internet Relay Chat Daemon | - * +------------------------------------+ - * - * InspIRCd: (C) 2002-2007 InspIRCd Development Team - * See: http://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; - } -} - +/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://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 diff --git a/src/modes/cmode_l.cpp b/src/modes/cmode_l.cpp index 1a57a440d..5e1a8c26c 100644 --- a/src/modes/cmode_l.cpp +++ b/src/modes/cmode_l.cpp @@ -1,97 +1 @@ -/* +------------------------------------+ - * | Inspire Internet Relay Chat Daemon | - * +------------------------------------+ - * - * InspIRCd: (C) 2002-2007 InspIRCd Development Team - * See: http://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; -} +/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://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 diff --git a/src/modes/cmode_m.cpp b/src/modes/cmode_m.cpp index 520248fda..da882333b 100644 --- a/src/modes/cmode_m.cpp +++ b/src/modes/cmode_m.cpp @@ -1,36 +1 @@ -/* +------------------------------------+ - * | Inspire Internet Relay Chat Daemon | - * +------------------------------------+ - * - * InspIRCd: (C) 2002-2007 InspIRCd Development Team - * See: http://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; - } -} - +/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://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 diff --git a/src/modes/cmode_n.cpp b/src/modes/cmode_n.cpp index ddc2e1bbd..3ae7d538c 100644 --- a/src/modes/cmode_n.cpp +++ b/src/modes/cmode_n.cpp @@ -1,36 +1 @@ -/* +------------------------------------+ - * | Inspire Internet Relay Chat Daemon | - * +------------------------------------+ - * - * InspIRCd: (C) 2002-2007 InspIRCd Development Team - * See: http://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; - } -} - +/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://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 diff --git a/src/modes/cmode_o.cpp b/src/modes/cmode_o.cpp index 47d191ff8..9ae18109e 100644 --- a/src/modes/cmode_o.cpp +++ b/src/modes/cmode_o.cpp @@ -1,153 +1 @@ -/* +------------------------------------+ - * | Inspire Internet Relay Chat Daemon | - * +------------------------------------+ - * - * InspIRCd: (C) 2002-2007 InspIRCd Development Team - * See: http://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 ""; -} +/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://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 diff --git a/src/modes/cmode_p.cpp b/src/modes/cmode_p.cpp index 15c33222e..c762b7827 100644 --- a/src/modes/cmode_p.cpp +++ b/src/modes/cmode_p.cpp @@ -1,35 +1 @@ -/* +------------------------------------+ - * | Inspire Internet Relay Chat Daemon | - * +------------------------------------+ - * - * InspIRCd: (C) 2002-2007 InspIRCd Development Team - * See: http://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; - } -} +/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://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 diff --git a/src/modes/cmode_s.cpp b/src/modes/cmode_s.cpp index 135291592..069591e6b 100644 --- a/src/modes/cmode_s.cpp +++ b/src/modes/cmode_s.cpp @@ -1,35 +1 @@ -/* +------------------------------------+ - * | Inspire Internet Relay Chat Daemon | - * +------------------------------------+ - * - * InspIRCd: (C) 2002-2007 InspIRCd Development Team - * See: http://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; - } -} +/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://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 diff --git a/src/modes/cmode_t.cpp b/src/modes/cmode_t.cpp index 2a6c06b42..def48a781 100644 --- a/src/modes/cmode_t.cpp +++ b/src/modes/cmode_t.cpp @@ -1,36 +1 @@ -/* +------------------------------------+ - * | Inspire Internet Relay Chat Daemon | - * +------------------------------------+ - * - * InspIRCd: (C) 2002-2007 InspIRCd Development Team - * See: http://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; - } -} - +/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://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 diff --git a/src/modes/cmode_v.cpp b/src/modes/cmode_v.cpp index 1e244c606..c055cca7f 100644 --- a/src/modes/cmode_v.cpp +++ b/src/modes/cmode_v.cpp @@ -1,152 +1 @@ -/* +------------------------------------+ - * | Inspire Internet Relay Chat Daemon | - * +------------------------------------+ - * - * InspIRCd: (C) 2002-2007 InspIRCd Development Team - * See: http://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 ""; -} +/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://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 diff --git a/src/modes/umode_i.cpp b/src/modes/umode_i.cpp index 5a9327375..6bd769a9a 100644 --- a/src/modes/umode_i.cpp +++ b/src/modes/umode_i.cpp @@ -1,45 +1 @@ -/* +------------------------------------+ - * | Inspire Internet Relay Chat Daemon | - * +------------------------------------+ - * - * InspIRCd: (C) 2002-2007 InspIRCd Development Team - * See: http://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; -} +/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://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 diff --git a/src/modes/umode_n.cpp b/src/modes/umode_n.cpp index c9c9e312e..c5d2599d8 100644 --- a/src/modes/umode_n.cpp +++ b/src/modes/umode_n.cpp @@ -1,58 +1 @@ -/* +------------------------------------+ - * | Inspire Internet Relay Chat Daemon | - * +------------------------------------+ - * - * InspIRCd: (C) 2002-2007 InspIRCd Development Team - * See: http://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; -} - +/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://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 diff --git a/src/modes/umode_o.cpp b/src/modes/umode_o.cpp index 30ed089f6..1b47fe8b2 100644 --- a/src/modes/umode_o.cpp +++ b/src/modes/umode_o.cpp @@ -1,49 +1 @@ -/* +------------------------------------+ - * | Inspire Internet Relay Chat Daemon | - * +------------------------------------+ - * - * InspIRCd: (C) 2002-2007 InspIRCd Development Team - * See: http://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(); -} +/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://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 diff --git a/src/modes/umode_s.cpp b/src/modes/umode_s.cpp index 4b3179001..f0441b85f 100644 --- a/src/modes/umode_s.cpp +++ b/src/modes/umode_s.cpp @@ -1,45 +1 @@ -/* +------------------------------------+ - * | Inspire Internet Relay Chat Daemon | - * +------------------------------------+ - * - * InspIRCd: (C) 2002-2007 InspIRCd Development Team - * See: http://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; -} +/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://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 diff --git a/src/modes/umode_w.cpp b/src/modes/umode_w.cpp index 383c91f6e..21c007655 100644 --- a/src/modes/umode_w.cpp +++ b/src/modes/umode_w.cpp @@ -1,46 +1 @@ -/* +------------------------------------+ - * | Inspire Internet Relay Chat Daemon | - * +------------------------------------+ - * - * InspIRCd: (C) 2002-2007 InspIRCd Development Team - * See: http://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; -} - +/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://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 diff --git a/src/modules.cpp b/src/modules.cpp index 9deaa7954..2c34a034c 100644 --- a/src/modules.cpp +++ b/src/modules.cpp @@ -1,732 +1 @@ -/* +------------------------------------+ - * | Inspire Internet Relay Chat Daemon | - * +------------------------------------+ - * - * InspIRCd: (C) 2002-2007 InspIRCd Development Team - * See: http://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 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(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(); -} - - +/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://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 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(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 diff --git a/src/modules/extra/README b/src/modules/extra/README index 7e3096b34..4c4beef9d 100644 --- a/src/modules/extra/README +++ b/src/modules/extra/README @@ -1,7 +1 @@ -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. +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 diff --git a/src/modules/extra/m_filter_pcre.cpp b/src/modules/extra/m_filter_pcre.cpp index 6fe79a981..0c6c05c8c 100644 --- a/src/modules/extra/m_filter_pcre.cpp +++ b/src/modules/extra/m_filter_pcre.cpp @@ -1,182 +1 @@ -/* +------------------------------------+ - * | Inspire Internet Relay Chat Daemon | - * +------------------------------------+ - * - * InspIRCd: (C) 2002-2007 InspIRCd Development Team - * See: http://www.inspircd.org/wiki/index.php/Credits - * - * This program is free but copyrighted software; see - * the file COPYING for details. - * - * --------------------------------------------------- - */ - -#include "inspircd.h" -#include -#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 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::iterator index = filters.begin(); index != filters.end(); index++) - { - /* Skip ones that dont apply to us */ - - if (!FilterBase::AppliesToMe(user, dynamic_cast(&(*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::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::iterator i = filters.begin(); i != filters.end(); i++) - { - this->SendFilter(proto, opaque, &(*i)); - } - } - - virtual std::pair AddFilter(const std::string &freeform, const std::string &type, const std::string &reason, long duration, const std::string &flags) - { - for (std::vector::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::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); - +/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include #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 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::iterator index = filters.begin(); index != filters.end(); index++) { /* Skip ones that dont apply to us */ if (!FilterBase::AppliesToMe(user, dynamic_cast(&(*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::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::iterator i = filters.begin(); i != filters.end(); i++) { this->SendFilter(proto, opaque, &(*i)); } } virtual std::pair AddFilter(const std::string &freeform, const std::string &type, const std::string &reason, long duration, const std::string &flags) { for (std::vector::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::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 diff --git a/src/modules/extra/m_httpclienttest.cpp b/src/modules/extra/m_httpclienttest.cpp index 90e7a5159..3f74b549b 100644 --- a/src/modules/extra/m_httpclienttest.cpp +++ b/src/modules/extra/m_httpclienttest.cpp @@ -1,81 +1 @@ -/* +------------------------------------+ - * | Inspire Internet Relay Chat Daemon | - * +------------------------------------+ - * - * InspIRCd: (C) 2002-2007 InspIRCd Development Team - * See: http://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); - +/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://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 diff --git a/src/modules/extra/m_mysql.cpp b/src/modules/extra/m_mysql.cpp index 6605bed3c..eeabe5d48 100644 --- a/src/modules/extra/m_mysql.cpp +++ b/src/modules/extra/m_mysql.cpp @@ -1,889 +1 @@ -/* +------------------------------------+ - * | Inspire Internet Relay Chat Daemon | - * +------------------------------------+ - * - * InspIRCd: (C) 2002-2007 InspIRCd Development Team - * See: http://www.inspircd.org/wiki/index.php/Credits - * - * This program is free but copyrighted software; see - * the file COPYING for details. - * - * --------------------------------------------------- - */ - -#include "inspircd.h" -#include -#include -#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 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 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 colnames; - std::vector 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 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); - +/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include #include #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 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 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 colnames; std::vector 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 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 diff --git a/src/modules/extra/m_pgsql.cpp b/src/modules/extra/m_pgsql.cpp index 5d267fc1a..9e85a40de 100644 --- a/src/modules/extra/m_pgsql.cpp +++ b/src/modules/extra/m_pgsql.cpp @@ -1,984 +1 @@ -/* +------------------------------------+ - * | Inspire Internet Relay Chat Daemon | - * +------------------------------------+ - * - * InspIRCd: (C) 2002-2007 InspIRCd Development Team - * See: http://www.inspircd.org/wiki/index.php/Credits - * - * This program is free but copyrighted software; see - * the file COPYING for details. - * - * --------------------------------------------------- - */ - -#include "inspircd.h" -#include -#include -#include -#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 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 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); - +/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include #include #include #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 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 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 diff --git a/src/modules/extra/m_sqlauth.cpp b/src/modules/extra/m_sqlauth.cpp index 6b05ee521..862929919 100644 --- a/src/modules/extra/m_sqlauth.cpp +++ b/src/modules/extra/m_sqlauth.cpp @@ -1,194 +1 @@ -/* +------------------------------------+ - * | Inspire Internet Relay Chat Daemon | - * +------------------------------------+ - * - * InspIRCd: (C) 2002-2007 InspIRCd Development Team - * See: http://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(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); - +/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://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(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 diff --git a/src/modules/extra/m_sqlite3.cpp b/src/modules/extra/m_sqlite3.cpp index 735591496..6741d7745 100644 --- a/src/modules/extra/m_sqlite3.cpp +++ b/src/modules/extra/m_sqlite3.cpp @@ -1,660 +1 @@ -/* +------------------------------------+ - * | Inspire Internet Relay Chat Daemon | - * +------------------------------------+ - * - * InspIRCd: (C) 2002-2007 InspIRCd Development Team - * See: http://www.inspircd.org/wiki/index.php/Credits - * - * This program is free but copyrighted software; see - * the file COPYING for details. - * - * --------------------------------------------------- - */ - -#include "inspircd.h" -#include -#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 ConnMap; -typedef std::deque paramlist; -typedef std::deque 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 colnames; - std::vector 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); - +/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include #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 ConnMap; typedef std::deque paramlist; typedef std::deque 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 colnames; std::vector 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 diff --git a/src/modules/extra/m_sqllog.cpp b/src/modules/extra/m_sqllog.cpp index 391e4bbba..04eb1fef1 100644 --- a/src/modules/extra/m_sqllog.cpp +++ b/src/modules/extra/m_sqllog.cpp @@ -1,310 +1 @@ -/* +------------------------------------+ - * | Inspire Internet Relay Chat Daemon | - * +------------------------------------+ - * - * InspIRCd: (C) 2002-2007 InspIRCd Development Team - * See: http://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 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::iterator n; - - res = static_cast(request); - n = active_queries.find(res->id); - - if (n != active_queries.end()) - { - n->second->Go(res); - std::map::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); - +/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://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 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::iterator n; res = static_cast(request); n = active_queries.find(res->id); if (n != active_queries.end()) { n->second->Go(res); std::map::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 diff --git a/src/modules/extra/m_sqloper.cpp b/src/modules/extra/m_sqloper.cpp index 520869e21..4b09ac26e 100644 --- a/src/modules/extra/m_sqloper.cpp +++ b/src/modules/extra/m_sqloper.cpp @@ -1,283 +1 @@ -/* +------------------------------------+ - * | Inspire Internet Relay Chat Daemon | - * +------------------------------------+ - * - * InspIRCd: (C) 2002-2007 InspIRCd Development Team - * See: http://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(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); - +/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://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(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 diff --git a/src/modules/extra/m_sqlutils.cpp b/src/modules/extra/m_sqlutils.cpp index b470f99af..6cd09252b 100644 --- a/src/modules/extra/m_sqlutils.cpp +++ b/src/modules/extra/m_sqlutils.cpp @@ -1,238 +1 @@ -/* +------------------------------------+ - * | Inspire Internet Relay Chat Daemon | - * +------------------------------------+ - * - * InspIRCd: (C) 2002-2007 InspIRCd Development Team - * See: http://www.inspircd.org/wiki/index.php/Credits - * - * This program is free but copyrighted software; see - * the file COPYING for details. - * - * --------------------------------------------------- - */ - -#include "inspircd.h" -#include -#include -#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 IdUserMap; -typedef std::map IdChanMap; -typedef std::list 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 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); - +/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include #include #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 IdUserMap; typedef std::map IdChanMap; typedef std::list 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 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 diff --git a/src/modules/extra/m_sqlutils.h b/src/modules/extra/m_sqlutils.h index 92fbdf5c7..cdde51f67 100644 --- a/src/modules/extra/m_sqlutils.h +++ b/src/modules/extra/m_sqlutils.h @@ -1,143 +1 @@ -/* +------------------------------------+ - * | Inspire Internet Relay Chat Daemon | - * +------------------------------------+ - * - * InspIRCd: (C) 2002-2007 InspIRCd 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 +/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd 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 diff --git a/src/modules/extra/m_sqlv2.h b/src/modules/extra/m_sqlv2.h index c7f6edbb9..decac4b57 100644 --- a/src/modules/extra/m_sqlv2.h +++ b/src/modules/extra/m_sqlv2.h @@ -1,605 +1 @@ -/* +------------------------------------+ - * | Inspire Internet Relay Chat Daemon | - * +------------------------------------+ - * - * InspIRCd: (C) 2002-2007 InspIRCd 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 -#include -#include -#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 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) 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 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 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 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 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 config line and is useful - * for storing in a map and iterating on rehash to see which - * 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 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 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 +/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd 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 #include #include #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 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) 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 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 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 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 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 config line and is useful * for storing in a map and iterating on rehash to see which * 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 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 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 diff --git a/src/modules/extra/m_ssl_gnutls.cpp b/src/modules/extra/m_ssl_gnutls.cpp index fd8b12d32..037d2cf72 100644 --- a/src/modules/extra/m_ssl_gnutls.cpp +++ b/src/modules/extra/m_ssl_gnutls.cpp @@ -1,843 +1 @@ -/* +------------------------------------+ - * | Inspire Internet Relay Chat Daemon | - * +------------------------------------+ - * - * InspIRCd: (C) 2002-2007 InspIRCd Development Team - * See: http://www.inspircd.org/wiki/index.php/Credits - * - * This program is free but copyrighted software; see - * the file COPYING for details. - * - * --------------------------------------------------- - */ - -#include "inspircd.h" - -#include -#include - -#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 &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 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 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* metadata = new std::deque; - 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); - +/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include #include #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 &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 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 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* metadata = new std::deque; 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 diff --git a/src/modules/extra/m_ssl_openssl.cpp b/src/modules/extra/m_ssl_openssl.cpp index ffd9d4032..43dc43aea 100644 --- a/src/modules/extra/m_ssl_openssl.cpp +++ b/src/modules/extra/m_ssl_openssl.cpp @@ -1,901 +1 @@ -/* +------------------------------------+ - * | Inspire Internet Relay Chat Daemon | - * +------------------------------------+ - * - * InspIRCd: (C) 2002-2007 InspIRCd Development Team - * See: http://www.inspircd.org/wiki/index.php/Credits - * - * This program is free but copyrighted software; see - * the file COPYING for details. - * - * --------------------------------------------------- - */ - -#include "inspircd.h" - -#include -#include - -#ifdef WINDOWS -#include -#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 &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 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 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* metadata = new std::deque; - 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); - +/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include #include #ifdef WINDOWS #include #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 &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 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 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* metadata = new std::deque; 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 diff --git a/src/modules/extra/m_ssl_oper_cert.cpp b/src/modules/extra/m_ssl_oper_cert.cpp index c67b50c8c..7b1c90868 100644 --- a/src/modules/extra/m_ssl_oper_cert.cpp +++ b/src/modules/extra/m_ssl_oper_cert.cpp @@ -1,180 +1 @@ -/* +------------------------------------+ - * | Inspire Internet Relay Chat Daemon | - * +------------------------------------+ - * - * InspIRCd: (C) 2002-2007 InspIRCd 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 = ""; - } - - 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); - +/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd 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 = ""; } 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 diff --git a/src/modules/extra/m_sslinfo.cpp b/src/modules/extra/m_sslinfo.cpp index dc9274f1e..83de798c8 100644 --- a/src/modules/extra/m_sslinfo.cpp +++ b/src/modules/extra/m_sslinfo.cpp @@ -1,94 +1 @@ -/* +------------------------------------+ - * | Inspire Internet Relay Chat Daemon | - * +------------------------------------+ - * - * InspIRCd: (C) 2002-2007 InspIRCd Development Team - * See: http://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 = ""; - } - - 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); - +/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://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 = ""; } 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 diff --git a/src/modules/extra/m_testclient.cpp b/src/modules/extra/m_testclient.cpp index f4e58b7b5..a867dad20 100644 --- a/src/modules/extra/m_testclient.cpp +++ b/src/modules/extra/m_testclient.cpp @@ -1,110 +1 @@ -/* +------------------------------------+ - * | Inspire Internet Relay Chat Daemon | - * +------------------------------------+ - * - * InspIRCd: (C) 2002-2007 InspIRCd Development Team - * See: http://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); - +/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://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 diff --git a/src/modules/extra/m_ziplink.cpp b/src/modules/extra/m_ziplink.cpp index e815d1042..2a127258d 100644 --- a/src/modules/extra/m_ziplink.cpp +++ b/src/modules/extra/m_ziplink.cpp @@ -1,452 +1 @@ -/* +------------------------------------+ - * | Inspire Internet Relay Chat Daemon | - * +------------------------------------+ - * - * InspIRCd: (C) 2002-2007 InspIRCd Development Team - * See: http://www.inspircd.org/wiki/index.php/Credits - * - * This program is free but copyrighted software; see - * the file COPYING for details. - * - * --------------------------------------------------- - */ - -#include "inspircd.h" -#include -#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); - +/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include #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 diff --git a/src/modules/httpclient.h b/src/modules/httpclient.h index c5e84261f..109bfa666 100644 --- a/src/modules/httpclient.h +++ b/src/modules/httpclient.h @@ -1,127 +1 @@ -/* +------------------------------------+ - * | Inspire Internet Relay Chat Daemon | - * +------------------------------------+ - * - * InspIRCd: (C) 2002-2007 InspIRCd Development 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 -#include - -typedef std::map 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 +/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development 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 #include typedef std::map 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 diff --git a/src/modules/httpd.h b/src/modules/httpd.h index a8b0bafcd..32bac757f 100644 --- a/src/modules/httpd.h +++ b/src/modules/httpd.h @@ -1,166 +1 @@ -/* +------------------------------------+ - * | Inspire Internet Relay Chat Daemon | - * +------------------------------------+ - * - * InspIRCd: (C) 2002-2007 InspIRCd Development 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 -#include - -/** 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 - +/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development 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 #include /** 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 diff --git a/src/modules/m_alias.cpp b/src/modules/m_alias.cpp index 94c64b405..039aeed92 100644 --- a/src/modules/m_alias.cpp +++ b/src/modules/m_alias.cpp @@ -1,272 +1 @@ -/* +------------------------------------+ - * | Inspire Internet Relay Chat Daemon | - * +------------------------------------+ - * - * InspIRCd: (C) 2002-2007 InspIRCd Development Team - * See: http://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 Aliases; - std::map AliasMap; - std::vector 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) +/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://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 Aliases; std::map AliasMap; std::vector 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 diff --git a/src/modules/m_alltime.cpp b/src/modules/m_alltime.cpp index 97ab6a3fe..6a3f27ba8 100644 --- a/src/modules/m_alltime.cpp +++ b/src/modules/m_alltime.cpp @@ -1,83 +1 @@ -/* +------------------------------------+ - * | Inspire Internet Relay Chat Daemon | - * +------------------------------------+ - * - * InspIRCd: (C) 2002-2007 InspIRCd Development Team - * See: http://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 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) +/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://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 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 diff --git a/src/modules/m_antibear.cpp b/src/modules/m_antibear.cpp index 2718cbb4c..d95c70282 100644 --- a/src/modules/m_antibear.cpp +++ b/src/modules/m_antibear.cpp @@ -1,78 +1 @@ -/* +------------------------------------+ - * | Inspire Internet Relay Chat Daemon | - * +------------------------------------+ - * - * InspIRCd: (C) 2002-2007 InspIRCd Development Team - * See: http://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) +/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://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 diff --git a/src/modules/m_antibottler.cpp b/src/modules/m_antibottler.cpp index 3aa5592cc..907cfb39d 100644 --- a/src/modules/m_antibottler.cpp +++ b/src/modules/m_antibottler.cpp @@ -1,99 +1 @@ -/* +------------------------------------+ - * | Inspire Internet Relay Chat Daemon | - * +------------------------------------+ - * - * InspIRCd: (C) 2002-2007 InspIRCd Development Team - * See: http://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) +/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://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 diff --git a/src/modules/m_auditorium.cpp b/src/modules/m_auditorium.cpp index 6d709e431..419738ea7 100644 --- a/src/modules/m_auditorium.cpp +++ b/src/modules/m_auditorium.cpp @@ -1,191 +1 @@ -/* +------------------------------------+ - * | Inspire Internet Relay Chat Daemon | - * +------------------------------------+ - * - * InspIRCd: (C) 2002-2007 InspIRCd Development Team - * See: http://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 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::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) +/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://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 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::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 diff --git a/src/modules/m_banexception.cpp b/src/modules/m_banexception.cpp index 0cd03a08b..8a73e6070 100644 --- a/src/modules/m_banexception.cpp +++ b/src/modules/m_banexception.cpp @@ -1,153 +1 @@ -/* +------------------------------------+ - * | Inspire Internet Relay Chat Daemon | - * +------------------------------------+ - * - * InspIRCd: (C) 2002-2007 InspIRCd Development Team - * See: http://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, 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) +/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://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, 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 diff --git a/src/modules/m_banredirect.cpp b/src/modules/m_banredirect.cpp index 1764e6f56..2a0ed2ded 100644 --- a/src/modules/m_banredirect.cpp +++ b/src/modules/m_banredirect.cpp @@ -1,343 +1 @@ -/* +------------------------------------+ - * | Inspire Internet Relay Chat Daemon | - * +------------------------------------+ - * - * InspIRCd: (C) 2002-2007 InspIRCd Development Team - * See: http://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 BanRedirectList; -typedef std::deque 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(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(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) +/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://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 BanRedirectList; typedef std::deque 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(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(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 diff --git a/src/modules/m_blockamsg.cpp b/src/modules/m_blockamsg.cpp index 69e99d0bb..5ff0c1100 100644 --- a/src/modules/m_blockamsg.cpp +++ b/src/modules/m_blockamsg.cpp @@ -1,191 +1 @@ -/* +------------------------------------+ - * | Inspire Internet Relay Chat Daemon | - * +------------------------------------+ - * - * InspIRCd: (C) 2002-2007 InspIRCd Development Team - * See: http://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) +/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://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 diff --git a/src/modules/m_blockcaps.cpp b/src/modules/m_blockcaps.cpp index 9197a8f11..8713f8c0d 100644 --- a/src/modules/m_blockcaps.cpp +++ b/src/modules/m_blockcaps.cpp @@ -1,143 +1 @@ -/* +------------------------------------+ - * | Inspire Internet Relay Chat Daemon | - * +------------------------------------+ - * - * InspIRCd: (C) 2002-2007 InspIRCd Development Team - * See: http://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, " out of range, setting to default of 100."); - percent = 100; - } - if (minlen < 0 || minlen > MAXBUF-1) - { - ServerInstance->Log(DEFAULT, " 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) +/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://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, " out of range, setting to default of 100."); percent = 100; } if (minlen < 0 || minlen > MAXBUF-1) { ServerInstance->Log(DEFAULT, " 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 diff --git a/src/modules/m_blockcolor.cpp b/src/modules/m_blockcolor.cpp index 69b0e4686..0646caa0b 100644 --- a/src/modules/m_blockcolor.cpp +++ b/src/modules/m_blockcolor.cpp @@ -1,118 +1 @@ -/* +------------------------------------+ - * | Inspire Internet Relay Chat Daemon | - * +------------------------------------+ - * - * InspIRCd: (C) 2002-2007 InspIRCd Development Team - * See: http://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) +/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://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 diff --git a/src/modules/m_botmode.cpp b/src/modules/m_botmode.cpp index a6cad9577..8cc999f12 100644 --- a/src/modules/m_botmode.cpp +++ b/src/modules/m_botmode.cpp @@ -1,95 +1 @@ -/* +------------------------------------+ - * | Inspire Internet Relay Chat Daemon | - * +------------------------------------+ - * - * InspIRCd: (C) 2002-2007 InspIRCd Development Team - * See: http://www.inspircd.org/wiki/index.php/Credits - * - * This program is free but copyrighted software; see - * the file COPYING for details. - * - * --------------------------------------------------- - */ - -#include "inspircd.h" -#include -#include -#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) +/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include #include #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 diff --git a/src/modules/m_cban.cpp b/src/modules/m_cban.cpp index c8e6a86b9..65be0d9bb 100644 --- a/src/modules/m_cban.cpp +++ b/src/modules/m_cban.cpp @@ -1,251 +1 @@ -/* +------------------------------------+ - * | Inspire Internet Relay Chat Daemon | - * +------------------------------------+ - * - * InspIRCd: (C) 2002-2007 InspIRCd Development Team - * See: http://www.inspircd.org/wiki/index.php/Credits - * - * This program is free but copyrighted software; see - * the file COPYING for details. - * - * --------------------------------------------------- - */ - -#include "inspircd.h" -#include -#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 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 = " [ :]"; - } - - 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) - +/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include #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 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 = " [ :]"; } 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 diff --git a/src/modules/m_censor.cpp b/src/modules/m_censor.cpp index f4a5bd620..a7aa2f8b1 100644 --- a/src/modules/m_censor.cpp +++ b/src/modules/m_censor.cpp @@ -1,196 +1 @@ -/* +------------------------------------+ - * | Inspire Internet Relay Chat Daemon | - * +------------------------------------+ - * - * InspIRCd: (C) 2002-2007 InspIRCd 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 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 - 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) +/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd 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 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 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 diff --git a/src/modules/m_cgiirc.cpp b/src/modules/m_cgiirc.cpp index 290f55d22..64fd6c69f 100644 --- a/src/modules/m_cgiirc.cpp +++ b/src/modules/m_cgiirc.cpp @@ -1,511 +1 @@ -/* +------------------------------------+ - * | Inspire Internet Relay Chat Daemon | - * +------------------------------------+ - * - * InspIRCd: (C) 2002-2007 InspIRCd Development Team - * See: http://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 -#include -#include -#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 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 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) +/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://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 #include #include #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 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 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 diff --git a/src/modules/m_chancreate.cpp b/src/modules/m_chancreate.cpp index 915e7c8cb..8837db9c5 100644 --- a/src/modules/m_chancreate.cpp +++ b/src/modules/m_chancreate.cpp @@ -1,55 +1 @@ -/* +------------------------------------+ - * | Inspire Internet Relay Chat Daemon | - * +------------------------------------+ - * - * InspIRCd: (C) 2002-2007 InspIRCd Development Team - * See: http://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) +/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://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 diff --git a/src/modules/m_chanfilter.cpp b/src/modules/m_chanfilter.cpp index 375fbce9c..44aac9dae 100644 --- a/src/modules/m_chanfilter.cpp +++ b/src/modules/m_chanfilter.cpp @@ -1,155 +1 @@ -/* +------------------------------------+ - * | Inspire Internet Relay Chat Daemon | - * +------------------------------------+ - * - * InspIRCd: (C) 2002-2007 InspIRCd 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) +/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd 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 diff --git a/src/modules/m_chanprotect.cpp b/src/modules/m_chanprotect.cpp index 87bc1ca4c..74640fe52 100644 --- a/src/modules/m_chanprotect.cpp +++ b/src/modules/m_chanprotect.cpp @@ -1,531 +1 @@ -/* +------------------------------------+ - * | Inspire Internet Relay Chat Daemon | - * +------------------------------------+ - * - * InspIRCd: (C) 2002-2007 InspIRCd Development Team - * See: http://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 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 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) +/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://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 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 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 diff --git a/src/modules/m_check.cpp b/src/modules/m_check.cpp index 643af8e15..c99e985cc 100644 --- a/src/modules/m_check.cpp +++ b/src/modules/m_check.cpp @@ -1,188 +1 @@ -/* +------------------------------------+ - * | Inspire Internet Relay Chat Daemon | - * +------------------------------------+ - * - * InspIRCd: (C) 2002-2007 InspIRCd Development Team - * See: http://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 = "|||"; - } - - 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 - * :server.name 304 target :CHECK - * :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) +/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://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 = "|||"; } 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 * :server.name 304 target :CHECK * :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 diff --git a/src/modules/m_chghost.cpp b/src/modules/m_chghost.cpp index 0ec88d7e1..9fb751b8e 100644 --- a/src/modules/m_chghost.cpp +++ b/src/modules/m_chghost.cpp @@ -1,120 +1 @@ -/* +------------------------------------+ - * | Inspire Internet Relay Chat Daemon | - * +------------------------------------+ - * - * InspIRCd: (C) 2002-2007 InspIRCd Development Team - * See: http://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 = " "; - } - - 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) +/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://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 = " "; } 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 diff --git a/src/modules/m_chgident.cpp b/src/modules/m_chgident.cpp index 168adcc93..fb909b7ff 100644 --- a/src/modules/m_chgident.cpp +++ b/src/modules/m_chgident.cpp @@ -1,92 +1 @@ -/* +------------------------------------+ - * | Inspire Internet Relay Chat Daemon | - * +------------------------------------+ - * - * InspIRCd: (C) 2002-2007 InspIRCd Development Team - * See: http://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 = " "; - } - - 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) - +/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://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 = " "; } 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 diff --git a/src/modules/m_chgname.cpp b/src/modules/m_chgname.cpp index a4a31714b..0bf9004dd 100644 --- a/src/modules/m_chgname.cpp +++ b/src/modules/m_chgname.cpp @@ -1,89 +1 @@ -/* +------------------------------------+ - * | Inspire Internet Relay Chat Daemon | - * +------------------------------------+ - * - * InspIRCd: (C) 2002-2007 InspIRCd Development Team - * See: http://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 = " "; - } - - 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) +/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://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 = " "; } 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 diff --git a/src/modules/m_cloaking.cpp b/src/modules/m_cloaking.cpp index dfa5ee4e8..d7992301c 100644 --- a/src/modules/m_cloaking.cpp +++ b/src/modules/m_cloaking.cpp @@ -1,315 +1 @@ -/* +------------------------------------+ - * | Inspire Internet Relay Chat Daemon | - * +------------------------------------+ - * - * InspIRCd: (C) 2002-2007 InspIRCd Development Team - * See: http://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 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) +/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://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 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 diff --git a/src/modules/m_clones.cpp b/src/modules/m_clones.cpp index 72a7ca7e8..429b9c2b9 100644 --- a/src/modules/m_clones.cpp +++ b/src/modules/m_clones.cpp @@ -1,100 +1 @@ -/* +------------------------------------+ - * | Inspire Internet Relay Chat Daemon | - * +------------------------------------+ - * - * InspIRCd: (C) 2002-2007 InspIRCd Development Team - * See: http://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 = ""; - } - - 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 - * :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) +/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://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 = ""; } 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 * :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 diff --git a/src/modules/m_conn_join.cpp b/src/modules/m_conn_join.cpp index 2d639f310..a8e81fcd8 100644 --- a/src/modules/m_conn_join.cpp +++ b/src/modules/m_conn_join.cpp @@ -1,96 +1 @@ -/* +------------------------------------+ - * | Inspire Internet Relay Chat Daemon | - * +------------------------------------+ - * - * InspIRCd: (C) 2002-2007 InspIRCd Development Team - * See: http://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 Joinchans; - - - int tokenize(const string &str, std::vector &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::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) +/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://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 Joinchans; int tokenize(const string &str, std::vector &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::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 diff --git a/src/modules/m_conn_umodes.cpp b/src/modules/m_conn_umodes.cpp index 3f27eeff5..f9118a384 100644 --- a/src/modules/m_conn_umodes.cpp +++ b/src/modules/m_conn_umodes.cpp @@ -1,104 +1 @@ -/* +------------------------------------+ - * | Inspire Internet Relay Chat Daemon | - * +------------------------------------+ - * - * InspIRCd: (C) 2002-2007 InspIRCd Development Team - * See: http://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 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) +/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://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 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 diff --git a/src/modules/m_conn_waitpong.cpp b/src/modules/m_conn_waitpong.cpp index 0dd27ddbd..b84533f88 100644 --- a/src/modules/m_conn_waitpong.cpp +++ b/src/modules/m_conn_waitpong.cpp @@ -1,148 +1 @@ -/* +------------------------------------+ - * | Inspire Internet Relay Chat Daemon | - * +------------------------------------+ - * - * InspIRCd: (C) 2002-2007 InspIRCd Development Team - * See: http://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) +/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://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 diff --git a/src/modules/m_connflood.cpp b/src/modules/m_connflood.cpp index 47b19fdf4..71e52fd01 100644 --- a/src/modules/m_connflood.cpp +++ b/src/modules/m_connflood.cpp @@ -1,120 +1 @@ -/* +------------------------------------+ - * | Inspire Internet Relay Chat Daemon | - * +------------------------------------+ - * - * InspIRCd: (C) 2002-2007 InspIRCd Development Team - * See: http://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) +/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://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 diff --git a/src/modules/m_cycle.cpp b/src/modules/m_cycle.cpp index eb20f4f99..b1a22941d 100644 --- a/src/modules/m_cycle.cpp +++ b/src/modules/m_cycle.cpp @@ -1,99 +1 @@ -/* +------------------------------------+ - * | Inspire Internet Relay Chat Daemon | - * +------------------------------------+ - * - * InspIRCd: (C) 2002-2007 InspIRCd Development Team - * See: http://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 = " :[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) +/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2007 InspIRCd Development Team * See: http://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 = " :[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 diff --git a/src/modules/m_dccallow.cpp b/src/modules/m_dccallow.cpp index bfec3c5e1..61ef90d89 100644 --- a/src/modules/m_dccallow.cpp +++ b/src/modules/m_dccallow.cpp @@ -1,489 +1 @@ -/* +------------------------------------+ - * | Inspire Internet Relay Chat Daemon | - * +------------------------------------+ - * - * InspIRCd: (C) 2002-2007 InspIRCd Development Team - * See: http://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 userlist; -userlist ul; -typedef std::vector dccallowlist; -dccallowlist* dl; -typedef std::vector 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 = "{[+|-]