From 538d3ece4e737807e7d5a11849aab177b87c1571 Mon Sep 17 00:00:00 2001 From: prozacx Date: Tue, 24 Aug 2004 00:08:51 +0000 Subject: [PATCH] Initial revision git-svn-id: https://znc.svn.sourceforge.net/svnroot/znc/trunk@2 726aef4b-f618-498e-8847-2d620e286838 --- Buffer.cpp | 52 + Buffer.h | 45 + Chan.cpp | 311 ++++++ Chan.h | 123 +++ CreatePem.sh | 28 + Csocket.h | 2545 +++++++++++++++++++++++++++++++++++++++++++ DCCBounce.cpp | 130 +++ DCCBounce.h | 98 ++ DCCSock.cpp | 156 +++ DCCSock.h | 108 ++ IRCSock.cpp | 742 +++++++++++++ IRCSock.h | 72 ++ Makefile | 31 + Modules.cpp | 594 ++++++++++ Modules.h | 174 +++ Nick.cpp | 62 ++ Nick.h | 44 + Server.cpp | 17 + Server.h | 23 + Timers.h | 49 + User.cpp | 397 +++++++ User.h | 124 +++ UserSock.cpp | 846 ++++++++++++++ UserSock.h | 73 ++ Utils.cpp | 687 ++++++++++++ Utils.h | 291 +++++ main.cpp | 92 ++ main.h | 17 + md5.cpp | 283 +++++ md5.h | 56 + modules/buildmod | 45 + modules/email.cc | 269 +++++ modules/raw.cpp | 28 + modules/sample.cpp | 198 ++++ modules/savebuff.cc | 211 ++++ modules/schat.cc | 462 ++++++++ modules/shell.cpp | 220 ++++ znc.conf | 95 ++ znc.cpp | 471 ++++++++ znc.h | 68 ++ zncchk | 25 + 41 files changed, 10362 insertions(+) create mode 100644 Buffer.cpp create mode 100644 Buffer.h create mode 100644 Chan.cpp create mode 100644 Chan.h create mode 100755 CreatePem.sh create mode 100644 Csocket.h create mode 100644 DCCBounce.cpp create mode 100644 DCCBounce.h create mode 100644 DCCSock.cpp create mode 100644 DCCSock.h create mode 100644 IRCSock.cpp create mode 100644 IRCSock.h create mode 100644 Makefile create mode 100644 Modules.cpp create mode 100644 Modules.h create mode 100644 Nick.cpp create mode 100644 Nick.h create mode 100644 Server.cpp create mode 100644 Server.h create mode 100644 Timers.h create mode 100644 User.cpp create mode 100644 User.h create mode 100644 UserSock.cpp create mode 100644 UserSock.h create mode 100644 Utils.cpp create mode 100644 Utils.h create mode 100644 main.cpp create mode 100644 main.h create mode 100644 md5.cpp create mode 100644 md5.h create mode 100755 modules/buildmod create mode 100644 modules/email.cc create mode 100644 modules/raw.cpp create mode 100644 modules/sample.cpp create mode 100644 modules/savebuff.cc create mode 100644 modules/schat.cc create mode 100644 modules/shell.cpp create mode 100644 znc.conf create mode 100644 znc.cpp create mode 100644 znc.h create mode 100755 zncchk diff --git a/Buffer.cpp b/Buffer.cpp new file mode 100644 index 00000000..0d140179 --- /dev/null +++ b/Buffer.cpp @@ -0,0 +1,52 @@ +#include "Buffer.h" + +CBufLine::CBufLine(const string& sPre, const string& sPost) { + m_sPre = sPre; + m_sPost = sPost; +} + +CBufLine::~CBufLine() {} + +void CBufLine::GetLine(const string& sTarget, string& sRet) { + sRet = m_sPre + sTarget + m_sPost; +} + +CBuffer::CBuffer(unsigned int uLineCount) { + m_uLineCount = uLineCount; +} + +CBuffer::~CBuffer() {} + +int CBuffer::AddLine(const string& sPre, const string& sPost) { + if (!m_uLineCount) { + return 0; + } + + if (size() >= m_uLineCount) { + erase(begin()); + } + + push_back(CBufLine(sPre, sPost)); + return size(); +} + +bool CBuffer::GetLine(const string& sTarget, string& sRet, unsigned int uIdx) { + if (uIdx >= size()) { + return false; + } + + (*this)[uIdx].GetLine(sTarget, sRet); + return true; +} + +bool CBuffer::GetNextLine(const string& sTarget, string& sRet) { + sRet = ""; + + if (!size()) { + return false; + } + + begin()->GetLine(sTarget, sRet); + erase(begin()); + return true; +} diff --git a/Buffer.h b/Buffer.h new file mode 100644 index 00000000..09ddf271 --- /dev/null +++ b/Buffer.h @@ -0,0 +1,45 @@ +#ifndef _BUFFER_H +#define _BUFFER_H + +#include +#include +using std::vector; +using std::string; + +class CBufLine { +public: + CBufLine(const string& sPre, const string& sPost); + virtual ~CBufLine(); + void GetLine(const string& sTarget, string& sRet); + +private: +protected: + string m_sPre; + string m_sPost; +}; + +class CBuffer : private vector { +public: + CBuffer(unsigned int uLineCount = 100); + virtual ~CBuffer(); + + int AddLine(const string& sPre, const string& sPost); + bool GetNextLine(const string& sTarget, string& sRet); + bool GetLine(const string& sTarget, string& sRet, unsigned int uIdx); + bool IsEmpty() { return empty(); } + void Clear() { clear(); } + + // Setters + void SetLineCount(unsigned int u) { m_uLineCount = u; } + // !Setters + + // Getters + unsigned int GetLineCount() const { return m_uLineCount; } + // !Getters +private: +protected: + unsigned int m_uLineCount; +}; + +#endif // !_BUFFER_H + diff --git a/Chan.cpp b/Chan.cpp new file mode 100644 index 00000000..d22fbdad --- /dev/null +++ b/Chan.cpp @@ -0,0 +1,311 @@ +#include "Chan.h" +#include "znc.h" +#include "User.h" +#include "Utils.h" + +void CChan::Reset() { + m_bWhoDone = false; + m_bIsOn = false; + m_uOpCount = 0; + m_uVoiceCount = 0; + m_uModes = 0; + m_uLimit = 0; + m_uClientRequests = 0; + ClearNicks(); +} + +void CChan::Joined() { +} + +void CChan::Cycle() const { + m_pUser->PutIRC("PART " + GetName() + "\r\nJOIN " + GetName() + " " + GetKey()); +} + +string CChan::GetModeString() const { + string sRet; + + if (m_uModes & Secret) { sRet += "s"; } + if (m_uModes & Private) { sRet += "p"; } + if (m_uModes & OpTopic) { sRet += "t"; } + if (m_uModes & InviteOnly) { sRet += "i"; } + if (m_uModes & NoMessages) { sRet += "n"; } + if (m_uModes & Moderated) { sRet += "m"; } + if (m_uLimit) { sRet += "l"; } + if (m_uModes & Key) { sRet += "k"; } + + return (sRet.empty()) ? sRet : ("+" + sRet); +} + +void CChan::SetModes(const string& sModes) { + m_uModes = 0; + m_uLimit = 0; + m_sKey = ""; + ModeChange(sModes); +} + +void CChan::IncClientRequests() { + m_uClientRequests++; +} + +bool CChan::DecClientRequests() { + if (!m_uClientRequests) { + return false; + } + + m_uClientRequests--; + return true; +} + +bool CChan::Who() { + if (m_bWhoDone) { + return false; + } + + m_pUser->PutIRC("WHO " + GetName()); + return true; +} + +void CChan::OnWho(const string& sNick, const string& sIdent, const string& sHost) { + CNick* pNick = FindNick(sNick); + + if (pNick) { + pNick->SetIdent(sIdent); + pNick->SetHost(sHost); + } +} + +void CChan::ModeChange(const string& sModes, const string& sOpNick) { + string sModeArg = CUtils::Token(sModes, 0); + string sArgs = CUtils::Token(sModes, 1, true); + bool bAdd = true; + +#ifdef _MODULES + CNick* pNick = FindNick(sOpNick); + if (pNick) { + m_pUser->GetModules().OnRawMode(*pNick, *this, sModeArg, sArgs); + } +#endif + + for (unsigned int a = 0; a < sModeArg.size(); a++) { + switch (sModeArg[a]) { + case '+': bAdd = true; break; + case '-': bAdd = false; break; + case 's': if (bAdd) { m_uModes |= Secret; } else { m_uModes &= ~Secret; } break; + case 'p': if (bAdd) { m_uModes |= Private; } else { m_uModes &= ~Private; } break; + case 'm': if (bAdd) { m_uModes |= Moderated; } else { m_uModes &= ~Moderated; } break; + case 'i': if (bAdd) { m_uModes |= InviteOnly; } else { m_uModes &= ~InviteOnly; } break; + case 'n': if (bAdd) { m_uModes |= NoMessages; } else { m_uModes &= ~NoMessages; } break; + case 't': if (bAdd) { m_uModes |= OpTopic; } else { m_uModes &= ~OpTopic; } break; + case 'l': if (bAdd) { m_uLimit = strtoul(GetModeArg(sArgs).c_str(), NULL, 10); } else { m_uLimit = 0; } break; + case 'k': if (bAdd) { m_uModes |= Key; SetKey(GetModeArg(sArgs)); } else { m_uModes &= ~Key; GetModeArg(sArgs); } break; + case 'o': OnOp(sOpNick, GetModeArg(sArgs), bAdd); break; + case 'v': OnVoice(sOpNick, GetModeArg(sArgs), bAdd); break; + case 'b': // Don't do anything with bans yet + case 'e': // Don't do anything with excepts yet + default: GetModeArg(sArgs); // Pop off an arg, assume new modes will have an argument + } + } +} + +string CChan::GetModeArg(string& sArgs) const { + string sRet = sArgs.substr(0, sArgs.find(' ')); + sArgs = (sRet.size() < sArgs.size()) ? sArgs.substr(sRet.size() +1) : ""; + return sRet; +} + +void CChan::ClearNicks() { + for (map::iterator a = m_msNicks.begin(); a != m_msNicks.end(); a++) { + delete a->second; + } + + m_msNicks.clear(); +} + +int CChan::AddNicks(const string& sNicks) { + if (IsOn()) { + return 0; + } + + int iRet = 0; + string sCurNick; + + for (unsigned int a = 0; a < sNicks.size(); a++) { + switch (sNicks[a]) { + case ' ': + if (AddNick(sCurNick)) { + iRet++; + } + + sCurNick = ""; + + break; + default: + sCurNick += sNicks[a]; + break; + } + } + + if (!sCurNick.empty()) { + if (AddNick(sCurNick)) { + iRet++; + } + } + + return iRet; +} + +bool CChan::AddNick(const string& sNick) { + const char* p = sNick.c_str(); + bool bIsOp = false; + bool bIsVoice = false; + + switch (*p) { + case '\0': + return false; + case '@': + bIsOp = true; + case '+': + if (!*++p) { + return false; + } + + bIsVoice = !bIsOp; + } + + CNick* pNick = FindNick(p); + if (!pNick) { + pNick = new CNick(p); + } + + if ((bIsOp) && (!pNick->IsOp())) { + IncOpCount(); + pNick->SetOp(true); + } else if ((bIsVoice) && (!pNick->IsVoice())) { + IncVoiceCount(); + pNick->SetVoice(true); + } + + m_msNicks[pNick->GetNick()] = pNick; + + return true; +} + +bool CChan::RemNick(const string& sNick) { + map::iterator it = m_msNicks.find(sNick); + if (it == m_msNicks.end()) { + return false; + } + + if (it->second->IsOp()) { + DecOpCount(); + } + + if (it->second->IsVoice()) { + DecVoiceCount(); + } + + delete it->second; + m_msNicks.erase(it); + + if ((m_msNicks.size() == 1) && (!m_msNicks.begin()->second->IsOp())) { + Cycle(); + } + + return true; +} + +bool CChan::ChangeNick(const string& sOldNick, const string& sNewNick) { + map::iterator it = m_msNicks.find(sOldNick); + + if (it == m_msNicks.end()) { + return false; + } + + // Rename this nick + it->second->SetNick(sNewNick); + + // Insert a new element into the map then erase the old one, do this to change the key + m_msNicks[sNewNick] = it->second; + m_msNicks.erase(it); + + return true; +} + +void CChan::OnOp(const string& sOpNick, const string& sNick, bool bOpped) { + CNick* pNick = FindNick(sNick); + + if (pNick) { + bool bNoChange = (pNick->IsOp() == bOpped); +#ifdef _MODULES + CNick* pOpNick = FindNick(sOpNick); + + if (pOpNick) { + if (bOpped) { + m_pUser->GetModules().OnOp(*pOpNick, *pNick, *this, bNoChange); + } else { + m_pUser->GetModules().OnDeop(*pOpNick, *pNick, *this, bNoChange); + } + } +#endif + + if (bNoChange) { + // If no change, return + return; + } + + pNick->SetOp(bOpped); + (bOpped) ? IncOpCount() : DecOpCount(); + } +} + +void CChan::OnVoice(const string& sOpNick, const string& sNick, bool bVoiced) { + CNick* pNick = FindNick(sNick); + + if (pNick) { + bool bNoChange = (pNick->IsVoice() == bVoiced); + +#ifdef _MODULES + CNick* pOpNick = FindNick(sOpNick); + + if (pOpNick) { + if (bVoiced) { + m_pUser->GetModules().OnVoice(*pOpNick, *pNick, *this, bNoChange); + } else { + m_pUser->GetModules().OnDevoice(*pOpNick, *pNick, *this, bNoChange); + } + } +#endif + + if (bNoChange) { + // If no change, return + return; + } + + pNick->SetVoice(bVoiced); + (bVoiced) ? IncVoiceCount() : DecVoiceCount(); + } +} + +CNick* CChan::FindNick(const string& sNick) const { + map::const_iterator it = m_msNicks.find(sNick); + return (it != m_msNicks.end()) ? it->second : NULL; +} + +int CChan::AddBuffer(const string& sLine) { + // Todo: revisit the buffering + if (!m_uBufferCount) { + return 0; + } + + if (m_vsBuffer.size() >= m_uBufferCount) { + m_vsBuffer.erase(m_vsBuffer.begin()); + } + + m_vsBuffer.push_back(sLine); + return m_vsBuffer.size(); +} + +void CChan::ClearBuffer() { + m_vsBuffer.clear(); +} + diff --git a/Chan.h b/Chan.h new file mode 100644 index 00000000..3f1beba6 --- /dev/null +++ b/Chan.h @@ -0,0 +1,123 @@ +#ifndef _CHAN_H +#define _CHAN_H + +#include "Nick.h" +#include +#include +using std::vector; +using std::map; + +// Forward Declarations +class CUser; +// !Forward Declarations + +class CChan { +public: + typedef enum { + Private = 1 << 0, + Secret = 1 << 1, + NoMessages = 1 << 2, + Moderated = 1 << 3, + OpTopic = 1 << 4, + InviteOnly = 1 << 5, + Key = 1 << 6 + } EMode; + + CChan(const string& sName, CUser* pUser, unsigned int uBufferCount = 0) { + m_sName = sName; + m_pUser = pUser; + m_bKeepBuffer = false; + m_uBufferCount = uBufferCount; + Reset(); + } + virtual ~CChan() { + ClearNicks(); + } + + void Reset(); + void Joined(); + void Cycle() const; + + void IncClientRequests(); + bool DecClientRequests(); + + bool Who(); + void OnWho(const string& sNick, const string& sIdent, const string& sHost); + + // Modes + void SetModes(const string& s); + void ModeChange(const string& sModes, const string& sNick = ""); + void OnOp(const string& sOpNick, const string& sNick, bool bOpped); + void OnVoice(const string& sOpNick, const string& sNick, bool bVoiced); + string GetModeString() const; + string GetModeArg(string& sArgs) const; + // !Modes + + // Nicks + void ClearNicks(); + CNick* FindNick(const string& sNick) const; + int AddNicks(const string& sNicks); + bool AddNick(const string& sNick); + bool RemNick(const string& sNick); + bool ChangeNick(const string& sOldNick, const string& sNewNick); + // !Nicks + + // Buffer + int AddBuffer(const string& sLine); + void ClearBuffer(); + // !Buffer + + // Setters + void SetIsOn(bool b) { m_bIsOn = b; if (!b) { Reset(); } else { Joined(); } } + void SetKey(const string& s) { m_sKey = s; } + void SetTopic(const string& s) { m_sTopic = s; } + void SetDefaultModes(const string& s) { m_sDefaultModes = s; } + void IncOpCount() { m_uOpCount++; } + void DecOpCount() { m_uOpCount -= (m_uOpCount > 0); } + void IncVoiceCount() { m_uVoiceCount++; } + void DecVoiceCount() { m_uVoiceCount -= (m_uVoiceCount > 0); } + void SetBufferCount(unsigned int u) { m_uBufferCount = u; } + void SetKeepBuffer(bool b) { m_bKeepBuffer = b; } + void SetWhoDone(bool b = true) { m_bWhoDone = b; } + // !Setters + + // Getters + const bool IsOn() const { return m_bIsOn; } + const string& GetName() const { return m_sName; } + unsigned int GetModes() const { return m_uModes; } + const string& GetKey() const { return m_sKey; } + unsigned int GetLimit() const { return m_uLimit; } + const string& GetTopic() const { return m_sTopic; } + const string& GetDefaultModes() const { return m_sDefaultModes; } + const vector& GetBans() const { return m_vsBans; } + const vector& GetBuffer() const { return m_vsBuffer; } + const map& GetNicks() const { return m_msNicks; } + unsigned int GetNickCount() const { return m_msNicks.size(); } + unsigned int GetOpCount() const { return m_uOpCount; } + unsigned int GetVoiceCount() const { return m_uVoiceCount; } + unsigned int GetBufferCount() const { return m_uBufferCount; } + bool HasMode(EMode eMode) const { return (m_uModes & eMode); } + bool KeepBuffer() const { return m_bKeepBuffer; } + // !Getters +private: +protected: + bool m_bIsOn; + bool m_bWhoDone; + bool m_bKeepBuffer; + string m_sName; + string m_sKey; + string m_sTopic; + CUser* m_pUser; + unsigned int m_uLimit; + unsigned int m_uModes; + string m_sDefaultModes; + vector m_vsBans; + map m_msNicks; // Todo: make this caseless (irc style) + unsigned int m_uBufferCount; + unsigned int m_uOpCount; + unsigned int m_uVoiceCount; + unsigned int m_uClientRequests; // Used to tell how many times a client tried to join this chan + vector m_vsBuffer; +}; + +#endif // !_CHAN_H diff --git a/CreatePem.sh b/CreatePem.sh new file mode 100755 index 00000000..145ad11d --- /dev/null +++ b/CreatePem.sh @@ -0,0 +1,28 @@ +#!/bin/sh + +if [ "$1" = "" ]; then + echo CreatePem.sh file.pem + exit 1 +fi + +log () { + echo [`date`] $* +} + +log Creating Random File for key seed +dd if=/dev/urandom of=blah-1234.txt bs=1024k count=10 >/dev/null 2>&1 + +openssl genrsa -rand blah-1234.txt -out ${1}.key +openssl req -new -key ${1}.key -out ${1}.csr +openssl x509 -req -days 365 -in ${1}.csr -signkey ${1}.key -out ${1}.crt + +log Cleaning up +rm -f blah-1234.txt ${1}.csr +log done + +cat ${1}.key > $1 +cat ${1}.crt >> $1 + +rm ${1}.key ${1}.crt + +echo "Created $1" diff --git a/Csocket.h b/Csocket.h new file mode 100644 index 00000000..66050ab1 --- /dev/null +++ b/Csocket.h @@ -0,0 +1,2545 @@ +/** +* +* Copyright (c) 1999-2004 Jim Hull +* All rights reserved +* +* Redistribution and use in source and binary forms, with or without modification, +* are permitted provided that the following conditions are met: +* Redistributions of source code must retain the above copyright notice, this +* list of conditions and the following disclaimer. +* 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. +* Redistributions in any form must be accompanied by information on how to obtain +* complete source code for the DB software and any accompanying software that uses the DB software. +* The source code must either be included in the distribution or be available for no more than +* the cost of distribution plus a nominal fee, and must be freely redistributable +* under reasonable conditions. For an executable file, complete source code means the source +* code for all modules it contains. It does not include source code for modules or files +* that typically accompany the major components of the operating system on which the executable file runs. +* +* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, +* BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, +* OR NON-INFRINGEMENT, ARE DISCLAIMED. IN NO EVENT SHALL SLEEPYCAT SOFTWARE 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. +* +* +* $Revision$ +*/ + +#ifndef _HAS_CSOCKET_ +#define _HAS_CSOCKET_ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef HAVE_LIBSSL +#include +#include +#endif /* HAVE_LIBSSL */ + +#include +#include +#include +#include +#include +#include + + +#ifndef CS_STRING +# ifdef _HAS_CSTRING_ +# define CS_STRING Cstring +# else +# define CS_STRING string +# endif /* _HAS_CSTRING_ */ +#endif /* CS_STRING */ + +#ifndef CS_DEBUG +#ifdef __DEBUG__ +# define CS_DEBUG( f ) cerr << __FILE__ << ":" << __LINE__ << " " << f << endl +#else +# define CS_DEBUG(f) (void)0 +#endif /* __DEBUG__ */ +#endif /* CS_DEBUG */ + +#ifdef __DEBUG__ +# define PERROR( f ) __Perror( f ) +#else +# define PERROR( f ) (void)0 +#endif /* __DEBUG__ */ + +using namespace std; + +#ifndef _NO_CSOCKET_NS // some people may not want to use a namespace +namespace Csocket +{ +#endif /* _NO_CSOCKET_NS */ + const u_int CS_BLOCKSIZE = 4096; + template inline void CS_Delete( T * & p ) { if( p ) { delete p; p = NULL; } } + + // wrappers for FD_SET and such to work in templates + inline void TFD_ZERO( fd_set *set ) + { + FD_ZERO( set ); + } + + inline void TFD_SET( int iSock, fd_set *set ) + { + FD_SET( iSock, set ); + } + + inline bool TFD_ISSET( int iSock, fd_set *set ) + { + if ( FD_ISSET( iSock, set ) ) + return( true ); + + return( false ); + } + + inline void TFD_CLR( int iSock, fd_set *set ) + { + FD_CLR( iSock, set ); + } + + inline void __Perror( const CS_STRING & s ) + { + char buff[512]; + memset( (char *)buff, '\0', 512 ); + if ( strerror_r( errno, buff, 511 ) == 0 ) + CS_DEBUG( s << ": " << buff ); + else + CS_DEBUG( s << ": Unknown Error Occured" ); + } + inline unsigned long long millitime() + { + struct timeval tv; + unsigned long long iTime = 0; + gettimeofday( &tv, NULL ); + iTime = (unsigned long long )tv.tv_sec * 1000; + iTime += ( (unsigned long long)tv.tv_usec / 1000 ); + return( iTime ); + } + +#ifdef HAVE_LIBSSL + inline void SSLErrors() + { + unsigned long iSSLError = 0; + while( ( iSSLError = ERR_get_error() ) != 0 ) + { + char szError[512]; + memset( (char *) szError, '\0', 512 ); + ERR_error_string_n( iSSLError, szError, 511 ); + if ( strlen( szError ) > 0 ) + CS_DEBUG( szError ); + } + } +#endif /* HAVE_LIBSSL */ + + inline bool GetHostByName( const CS_STRING & sHostName, struct in_addr *paddr ) + { + bool bRet = false; + struct hostent *hent = NULL; +#ifdef __linux__ + char hbuff[2048]; + struct hostent hentbuff; + + int err; + for( u_int a = 0; a < 20; a++ ) + { + memset( (char *)hbuff, '\0', 2048 ); + int iRet = gethostbyname_r( sHostName.c_str(), &hentbuff, hbuff, 2048, &hent, &err ); + + if ( iRet == 0 ) + { + bRet = true; + break; + } + + if ( iRet != TRY_AGAIN ) + break; + + PERROR( "gethostbyname_r" ); + } + + if ( !hent ) + bRet = false; +#else + hent = gethostbyname( sHostName.c_str() ); + PERROR( "gethostbyname" ); + + if ( hent ) + bRet = true; + +#endif /* __linux__ */ + + if ( bRet ) + bcopy( hent->h_addr_list[0], &paddr->s_addr, 4 ); + + return( bRet ); + } + + /** + * @class CCron + * @brief this is the main cron job class + * + * You should derive from this class, and override RunJob() with your code + * @author Jim Hull + */ + + class CCron + { + public: + CCron() + { + m_iCycles = 0; + m_iMaxCycles = 0; + m_bActive = true; + m_iTime = 0; + m_iTimeSequence = 60; + m_bPause = false; + } + virtual ~CCron() {} + + //! This is used by the Job Manager, and not you directly + void run() + { + if ( m_bPause ) + return; + + if ( ( m_bActive ) && ( time( NULL ) >= m_iTime ) ) + { + + RunJob(); + + if ( ( m_iMaxCycles > 0 ) && ( ++m_iCycles >= m_iMaxCycles ) ) + m_bActive = false; + else + m_iTime = time( NULL ) + m_iTimeSequence; + } + } + + /** + * @TimeSequence how often to run in seconds + * @iMaxCycles how many times to run, 0 makes it run forever + */ + void StartMaxCycles( int TimeSequence, u_int iMaxCycles ) + { + m_iTimeSequence = TimeSequence; + m_iTime = time( NULL ) + m_iTimeSequence; + m_iMaxCycles = iMaxCycles; + } + + //! starts and runs infinity amount of times + void Start( int TimeSequence ) + { + m_iTimeSequence = TimeSequence; + m_iTime = time( NULL ) + m_iTimeSequence; + m_iMaxCycles = 0; + } + + //! call this to turn off your cron, it will be removed + void Stop() + { + m_bActive = false; + } + + //! pauses excution of your code in RunJob + void Pause() + { + m_bPause = true; + } + + //! removes the pause on RunJon + void UnPause() + { + m_bPause = false; + } + + int GetInterval() const { return( m_iTimeSequence ); } + u_int GetMaxCycles() const { return( m_iMaxCycles ); } + u_int GetCyclesLeft() const { return( ( m_iMaxCycles > m_iCycles ? ( m_iMaxCycles - m_iCycles ) : 0 ) ); } + + //! returns true if cron is active + bool isValid() { return( m_bActive ); } + + const CS_STRING & GetName() const { return( m_sName ); } + void SetName( const CS_STRING & sName ) { m_sName = sName; } + + protected: + + //! this is the method you should override + virtual void RunJob() { CS_DEBUG( "This should be overriden" ); } + + time_t m_iTime; + bool m_bActive, m_bPause; + int m_iTimeSequence; + u_int m_iMaxCycles, m_iCycles; + CS_STRING m_sName; + }; + + /** + * @class Csock + * @brief Basic Socket Class + * The most basic level socket class + * You can use this class directly for quick things + * or use the socket manager + * @see TSocketManager + * @author Jim Hull + */ + class Csock + { + public: + //! default constructor, sets a timeout of 60 seconds + Csock( int itimeout = 60 ) + { + Init( "", 0, itimeout ); + } + /** + * Advanced constructor, for creating a simple connection + * + * sHostname the hostname your are connecting to + * iport the port you are connectint to + * itimeout how long to wait before ditching the connection, default is 60 seconds + */ + Csock( const CS_STRING & sHostname, int iport, int itimeout = 60 ) + { + Init( sHostname, iport, itimeout ); + } + + //! override this for accept sockets + //! returning NULL will cause it to return a new instance of + //! type T + virtual Csock *GetSockObj( const CS_STRING & sHostname, int iPort ) + { + return( NULL ); + } + + virtual ~Csock() + { + if ( m_iReadSock != m_iWriteSock ) + { + close( m_iReadSock ); + close( m_iWriteSock ); + } else + close( m_iReadSock ); + + m_iReadSock = -1; + m_iWriteSock = -1; + +#ifdef HAVE_LIBSSL + FREE_SSL(); + FREE_CTX(); + +#endif /* HAVE_LIBSSL */ + // delete any left over crons + for( unsigned int i = 0; i < m_vcCrons.size(); i++ ) + CS_Delete( m_vcCrons[i] ); + } + + enum ETConn + { + OUTBOUND = 0, //!< outbound connection + LISTENER = 1, //!< a socket accepting connections + INBOUND = 2 //!< an inbound connection, passed from LISTENER + + }; + + enum EFRead + { + READ_EOF = 0, //!< End Of File, done reading + READ_ERR = -1, //!< Error on the socket, socket closed, done reading + READ_EAGAIN = -2, //!< Try to get data again + READ_CONNREFUSED = -3 //!< Connection Refused + + }; + + enum EFSelect + { + SEL_OK = 0, //!< Select passed ok + SEL_TIMEOUT = -1, //!< Select timed out + SEL_EAGAIN = -2, //!< Select wants you to try again + SEL_ERR = -3 //!< Select recieved an error + + }; + + enum ESSLMethod + { + SSL23 = 0, + SSL2 = 2, + SSL3 = 3 + + }; + + Csock & operator<<( const CS_STRING & s ) + { + Write( s ); + return( *this ); + } + + Csock & operator<<( ostream & ( *io )( ostream & ) ) + { + Write( "\r\n" ); + return( *this ); + } + + Csock & operator<<( int i ) + { + stringstream s; + s << i; + Write( s.str() ); + return( *this ); + } + Csock & operator<<( unsigned int i ) + { + stringstream s; + s << i; + Write( s.str() ); + return( *this ); + } + Csock & operator<<( long i ) + { + stringstream s; + s << i; + Write( s.str() ); + return( *this ); + } + Csock & operator<<( unsigned long i ) + { + stringstream s; + s << i; + Write( s.str() ); + return( *this ); + } + Csock & operator<<( unsigned long long i ) + { + stringstream s; + s << i; + Write( s.str() ); + return( *this ); + } + Csock & operator<<( float i ) + { + stringstream s; + s << i; + Write( s.str() ); + return( *this ); + } + Csock & operator<<( double i ) + { + stringstream s; + s << i; + Write( s.str() ); + return( *this ); + } + + /** + * Create the connection + * + * \param sBindHost the ip you want to bind to locally + * \return true on success + */ + virtual bool Connect( const CS_STRING & sBindHost = "" ) + { + // create the socket + m_iReadSock = m_iWriteSock = SOCKET(); + + if ( m_iReadSock == -1 ) + return( false ); + + m_address.sin_family = PF_INET; + m_address.sin_port = htons( m_iport ); + + if ( !GetHostByName( m_shostname, &(m_address.sin_addr) ) ) + return( false ); + + // bind to a hostname if requested + if ( !sBindHost.empty() ) + { + struct sockaddr_in vh; + + vh.sin_family = PF_INET; + vh.sin_port = htons( 0 ); + + if ( !GetHostByName( sBindHost, &(vh.sin_addr) ) ) + return( false ); + + // try to bind 3 times, otherwise exit failure + bool bBound = false; + for( int a = 0; a < 3; a++ ) + { + if ( bind( m_iReadSock, (struct sockaddr *) &vh, sizeof( vh ) ) == 0 ) + { + bBound = true; + break; + } + usleep( 5000 ); // quick pause, common lets BIND!)(!*! + } + + if ( !bBound ) + { + CS_DEBUG( "Failure to bind to " << sBindHost ); + return( false ); + } + } + + // set it none blocking + int fdflags = fcntl (m_iReadSock, F_GETFL, 0); + fcntl( m_iReadSock, F_SETFL, fdflags|O_NONBLOCK ); + m_iConnType = OUTBOUND; + + // connect + int ret = connect( m_iReadSock, (struct sockaddr *)&m_address, sizeof( m_address ) ); + if ( ( ret == -1 ) && ( errno != EINPROGRESS ) ) + { + CS_DEBUG( "Connect Failed. ERRNO [" << errno << "] FD [" << m_iReadSock << "]" ); + return( false ); + } + + if ( m_bBLOCK ) + { + // unset the flags afterwords, rather than have connect block + int fdflags = fcntl (m_iReadSock, F_GETFL, 0); + fdflags &= ~O_NONBLOCK; + fcntl( m_iReadSock, F_SETFL, fdflags ); + + } + + return( true ); + } + + /** + * WriteSelect on this socket + * Only good if JUST using this socket, otherwise use the TSocketManager + */ + virtual int WriteSelect() + { + struct timeval tv; + fd_set wfds; + + TFD_ZERO( &wfds ); + TFD_SET( m_iWriteSock, &wfds ); + + tv.tv_sec = m_itimeout; + tv.tv_usec = 0; + + int ret = select( FD_SETSIZE, NULL, &wfds, NULL, &tv ); + + if ( ret == 0 ) + return( SEL_TIMEOUT ); + + if ( ret == -1 ) + { + if ( errno == EINTR ) + return( SEL_EAGAIN ); + else + return( SEL_ERR ); + } + + return( SEL_OK ); + } + + /** + * ReadSelect on this socket + * Only good if JUST using this socket, otherwise use the TSocketManager + */ + virtual int ReadSelect() + { + struct timeval tv; + fd_set rfds; + + TFD_ZERO( &rfds ); + TFD_SET( m_iReadSock, &rfds ); + + tv.tv_sec = m_itimeout; + tv.tv_usec = 0; + + int ret = select( FD_SETSIZE, &rfds, NULL, NULL, &tv ); + + if ( ret == 0 ) + return( SEL_TIMEOUT ); + + if ( ret == -1 ) + { + if ( errno == EINTR ) + return( SEL_EAGAIN ); + else + return( SEL_ERR ); + } + + return( SEL_OK ); + } + + /** + * Listens for connections + * + * \param iPort the port to listen on + * \param iMaxConns the maximum amount of connections to allow + */ + virtual bool Listen( int iPort, int iMaxConns = SOMAXCONN, const CS_STRING & sBindHost = "", u_int iTimeout = 0 ) + { + m_iReadSock = m_iWriteSock = SOCKET( true ); + m_iConnType = LISTENER; + m_itimeout = iTimeout; + + if ( m_iReadSock == -1 ) + return( false ); + + m_address.sin_family = PF_INET; + if ( sBindHost.empty() ) + m_address.sin_addr.s_addr = htonl( INADDR_ANY ); + else + { + if ( !GetHostByName( sBindHost, &(m_address.sin_addr) ) ) + return( false ); + } + m_address.sin_port = htons( iPort ); + bzero(&(m_address.sin_zero), 8); + + if ( bind( m_iReadSock, (struct sockaddr *) &m_address, sizeof( m_address ) ) == -1 ) + return( false ); + + if ( listen( m_iReadSock, iMaxConns ) == -1 ) + return( false ); + + if ( !m_bBLOCK ) + { + // set it none blocking + int fdflags = fcntl ( m_iReadSock, F_GETFL, 0); + fcntl( m_iReadSock, F_SETFL, fdflags|O_NONBLOCK ); + } + + return( true ); + } + + //! Accept an inbound connection, this is used internally + virtual int Accept( CS_STRING & sHost, int & iRPort ) + { + struct sockaddr_in client; + socklen_t clen = sizeof(struct sockaddr); + + int iSock = accept( m_iReadSock , (struct sockaddr *) &client, &clen ); + if ( iSock != -1 ) + { + if ( !m_bBLOCK ) + { + // make it none blocking + int fdflags = fcntl (iSock, F_GETFL, 0); + fcntl( iSock, F_SETFL, fdflags|O_NONBLOCK ); + } + + getpeername( iSock, (struct sockaddr *) &client, &clen ); + + sHost = inet_ntoa( client.sin_addr ); + iRPort = ntohs( client.sin_port ); + + if ( !ConnectionFrom( sHost, iRPort ) ) + { + close( iSock ); + iSock = -1; + } + + } + + return( iSock ); + } + + //! Accept an inbound SSL connection, this is used internally and called after Accept + virtual bool AcceptSSL() + { +#ifdef HAVE_LIBSSL + if ( !m_ssl ) + if ( !SSLServerSetup() ) + return( false ); + + + int err = SSL_accept( m_ssl ); + + if ( err == 1 ) + { + m_bFullsslAccept = true; + return( true ); + } + + m_bFullsslAccept = false; + + int sslErr = SSL_get_error( m_ssl, err ); + + if ( ( sslErr == SSL_ERROR_WANT_READ ) || ( sslErr == SSL_ERROR_WANT_WRITE ) ) + return( true ); + + SSLErrors(); + +#endif /* HAVE_LIBSSL */ + + return( false ); + } + + //! This sets up the SSL Client, this is used internally + virtual bool SSLClientSetup() + { +#ifdef HAVE_LIBSSL + m_bssl = true; + FREE_SSL(); + FREE_CTX(); + + SSLeay_add_ssl_algorithms(); + + switch( m_iMethod ) + { + case SSL2: + m_ssl_method = SSLv2_client_method(); + if ( !m_ssl_method ) + { + CS_DEBUG( "WARNING: MakeConnection .... SSLv2_client_method failed!" ); + return( false ); + } + break; + + case SSL3: + m_ssl_method = SSLv3_client_method(); + if ( !m_ssl_method ) + { + CS_DEBUG( "WARNING: MakeConnection .... SSLv3_client_method failed!" ); + return( false ); + } + break; + + case SSL23: + default: + m_ssl_method = SSLv23_client_method(); + if ( !m_ssl_method ) + { + CS_DEBUG( "WARNING: MakeConnection .... SSLv23_client_method failed!" ); + return( false ); + } + break; + } + + SSL_load_error_strings (); + // wrap some warnings in here + m_ssl_ctx = SSL_CTX_new ( m_ssl_method ); + if ( !m_ssl_ctx ) + return( false ); + + if ( !m_sPemFile.empty() ) + { // are we sending a client cerificate ? + SSL_CTX_set_default_passwd_cb( m_ssl_ctx, PemPassCB ); + SSL_CTX_set_default_passwd_cb_userdata( m_ssl_ctx, (void *)this ); + + // + // set up the CTX + if ( SSL_CTX_use_certificate_file( m_ssl_ctx, m_sPemFile.c_str() , SSL_FILETYPE_PEM ) <= 0 ) + { + CS_DEBUG( "Error with PEM file [" << m_sPemFile << "]" ); + SSLErrors(); + } + + if ( SSL_CTX_use_PrivateKey_file( m_ssl_ctx, m_sPemFile.c_str(), SSL_FILETYPE_PEM ) <= 0 ) + { + CS_DEBUG( "Error with PEM file [" << m_sPemFile << "]" ); + SSLErrors(); + } + } + + m_ssl = SSL_new ( m_ssl_ctx ); + if ( !m_ssl ) + return( false ); + + SSL_set_rfd( m_ssl, m_iReadSock ); + SSL_set_wfd( m_ssl, m_iWriteSock ); + SSL_set_verify( m_ssl, SSL_VERIFY_PEER, CertVerifyCB ); + + return( true ); +#else + return( false ); + +#endif /* HAVE_LIBSSL */ + } + + //! This sets up the SSL Server, this is used internally + virtual bool SSLServerSetup() + { +#ifdef HAVE_LIBSSL + m_bssl = true; + FREE_SSL(); + FREE_CTX(); + + SSLeay_add_ssl_algorithms(); + + switch( m_iMethod ) + { + case SSL2: + m_ssl_method = SSLv2_server_method(); + if ( !m_ssl_method ) + { + CS_DEBUG( "WARNING: MakeConnection .... SSLv2_server_method failed!" ); + return( false ); + } + break; + + case SSL3: + m_ssl_method = SSLv3_server_method(); + if ( !m_ssl_method ) + { + CS_DEBUG( "WARNING: MakeConnection .... SSLv3_server_method failed!" ); + return( false ); + } + break; + + case SSL23: + default: + m_ssl_method = SSLv23_server_method(); + if ( !m_ssl_method ) + { + CS_DEBUG( "WARNING: MakeConnection .... SSLv23_server_method failed!" ); + return( false ); + } + break; + } + + SSL_load_error_strings (); + // wrap some warnings in here + m_ssl_ctx = SSL_CTX_new ( m_ssl_method ); + if ( !m_ssl_ctx ) + return( false ); + + // set the pemfile password + SSL_CTX_set_default_passwd_cb( m_ssl_ctx, PemPassCB ); + SSL_CTX_set_default_passwd_cb_userdata( m_ssl_ctx, (void *)this ); + + if ( ( m_sPemFile.empty() ) || ( access( m_sPemFile.c_str(), R_OK ) != 0 ) ) + { + CS_DEBUG( "There is a problem with [" << m_sPemFile << "]" ); + return( false ); + } + + // + // set up the CTX + if ( SSL_CTX_use_certificate_file( m_ssl_ctx, m_sPemFile.c_str() , SSL_FILETYPE_PEM ) <= 0 ) + { + CS_DEBUG( "Error with PEM file [" << m_sPemFile << "]" ); + SSLErrors(); + return( false ); + } + + if ( SSL_CTX_use_PrivateKey_file( m_ssl_ctx, m_sPemFile.c_str(), SSL_FILETYPE_PEM ) <= 0 ) + { + CS_DEBUG( "Error with PEM file [" << m_sPemFile << "]" ); + SSLErrors(); + return( false ); + } + + if ( SSL_CTX_set_cipher_list( m_ssl_ctx, m_sCipherType.c_str() ) <= 0 ) + { + CS_DEBUG( "Could not assign cipher [" << m_sCipherType << "]" ); + return( false ); + } + + // + // setup the SSL + m_ssl = SSL_new ( m_ssl_ctx ); + if ( !m_ssl ) + return( false ); + + // Call for client Verification + SSL_set_rfd( m_ssl, m_iReadSock ); + SSL_set_wfd( m_ssl, m_iWriteSock ); + SSL_set_accept_state( m_ssl ); + + if ( m_bRequireClientCert ) + SSL_set_verify( m_ssl, SSL_VERIFY_FAIL_IF_NO_PEER_CERT|SSL_VERIFY_PEER, CertVerifyCB ); + + return( true ); +#else + return( false ); +#endif /* HAVE_LIBSSL */ + } + + /** + * Create the SSL connection + * + * \param sBindhost the ip you want to bind to locally + * \return true on success + */ + virtual bool ConnectSSL( const CS_STRING & sBindhost = "" ) + { +#ifdef HAVE_LIBSSL + if ( m_iReadSock == -1 ) + if ( !Connect( sBindhost ) ) + return( false ); + + if ( !m_ssl ) + if ( !SSLClientSetup() ) + return( false ); + + bool bPass = true; + + if ( m_bBLOCK ) + { + int fdflags = fcntl ( m_iReadSock, F_GETFL, 0); + fcntl( m_iReadSock, F_SETFL, fdflags|O_NONBLOCK ); + } + + int iErr = SSL_connect( m_ssl ); + if ( iErr != 1 ) + { + int sslErr = SSL_get_error( m_ssl, iErr ); + + if ( ( sslErr != SSL_ERROR_WANT_READ ) && ( sslErr != SSL_ERROR_WANT_WRITE ) ) + bPass = false; + } else + bPass = true; + + if ( m_bBLOCK ) + { + // unset the flags afterwords, rather then have connect block + int fdflags = fcntl (m_iReadSock, F_GETFL, 0); + fdflags &= ~O_NONBLOCK; + fcntl( m_iReadSock, F_SETFL, fdflags ); + + } + + return( bPass ); +#else + return( false ); +#endif /* HAVE_LIBSSL */ + } + + + /** + * Write data to the socket + * if not all of the data is sent, it will be stored on + * an internal buffer, and tried again with next call to Write + * if the socket is blocking, it will send everything, its ok to check ernno after this (nothing else is processed) + * + * \param data the data to send + * \param len the length of data + * + */ + virtual bool Write( const char *data, int len ) + { + m_sSend.append( data, len ); + + if ( m_sSend.empty() ) + return( true ); + + if ( m_bBLOCK ) + { + if ( WriteSelect() != SEL_OK ) + return( false ); + + } + // rate shaping + u_int iBytesToSend = 0; + + if ( ( m_iMaxBytes > 0 ) && ( m_iMaxMilliSeconds > 0 ) ) + { + unsigned long long iNOW = millitime(); + // figure out the shaping here + // if NOW - m_iLastSendTime > m_iMaxMilliSeconds then send a full length of ( iBytesToSend ) + if ( ( iNOW - m_iLastSendTime ) > m_iMaxMilliSeconds ) + { + m_iLastSendTime = iNOW; + iBytesToSend = m_iMaxBytes; + m_iLastSend = 0; + + } else // otherwise send m_iMaxBytes - m_iLastSend + iBytesToSend = m_iMaxBytes - m_iLastSend; + + // take which ever is lesser + if ( m_sSend.length() < iBytesToSend ) + iBytesToSend = m_sSend.length(); + + // add up the bytes sent + m_iLastSend += iBytesToSend; + + // so, are we ready to send anything ? + if ( iBytesToSend == 0 ) + return( true ); + + } else + iBytesToSend = m_sSend.length(); + +#ifdef HAVE_LIBSSL + if ( m_bssl ) + { + + if ( m_sSSLBuffer.empty() ) // on retrying to write data, ssl wants the data in the SAME spot and the SAME size + m_sSSLBuffer.append( m_sSend.data(), iBytesToSend ); + + int iErr = SSL_write( m_ssl, m_sSSLBuffer.data(), m_sSSLBuffer.length() ); + + if ( ( iErr < 0 ) && ( errno == ECONNREFUSED ) ) + { + // If ret == -1, the underlying BIO reported an I/O error (man SSL_get_error) + ConnectionRefused(); + return( false ); + } + + switch( SSL_get_error( m_ssl, iErr ) ) + { + case SSL_ERROR_NONE: + m_bsslEstablished = true; + // all ok + break; + + case SSL_ERROR_ZERO_RETURN: + { + // weird closer alert + return( false ); + } + + case SSL_ERROR_WANT_READ: + // retry + break; + + case SSL_ERROR_WANT_WRITE: + // retry + break; + + case SSL_ERROR_SSL: + { + SSLErrors(); + return( false ); + } + } + + if ( iErr > 0 ) + { + m_sSSLBuffer.clear(); + m_sSend.erase( 0, iErr ); + // reset the timer on successful write (we have to set it here because the write + // bit might not always be set, so need to trigger) + ResetTimer(); + m_iBytesWritten += (unsigned long long)iErr; + } + + return( true ); + } +#endif /* HAVE_LIBSSL */ + int bytes = write( m_iWriteSock, m_sSend.data(), iBytesToSend ); + + if ( ( bytes == -1 ) && ( errno == ECONNREFUSED ) ) + { + ConnectionRefused(); + return( false ); + } + + if ( ( bytes <= 0 ) && ( errno != EAGAIN ) ) + return( false ); + + // delete the bytes we sent + if ( bytes > 0 ) + { + m_sSend.erase( 0, bytes ); + ResetTimer(); // reset the timer on successful write + m_iBytesWritten += (unsigned long long)bytes; + } + + return( true ); + } + + /** + * convience function + * @see Write( const char *, int ) + */ + virtual bool Write( const CS_STRING & sData ) + { + return( Write( sData.c_str(), sData.length() ) ); + } + + /** + * Read from the socket + * Just pass in a pointer, big enough to hold len bytes + * + * \param data the buffer to read into + * \param len the size of the buffer + * + * \return + * Returns READ_EOF for EOF + * Returns READ_ERR for ERROR + * Returns READ_EAGAIN for Try Again ( EAGAIN ) + * Returns READ_CONNREFUSED for connection refused + * Otherwise returns the bytes read into data + */ + virtual int Read( char *data, int len ) + { + + if ( m_bBLOCK ) + { + if ( ReadSelect() != SEL_OK ) + return( READ_ERR ); + } + + int bytes = 0; + memset( (char *)data, '\0', len ); + +#ifdef HAVE_LIBSSL + if ( m_bssl ) + bytes = SSL_read( m_ssl, data, len ); + else +#endif /* HAVE_LIBSSL */ + bytes = read( m_iReadSock, data, len ); + + if ( bytes == -1 ) + { + if ( errno == ECONNREFUSED ) + return( READ_CONNREFUSED ); + + if ( ( errno == EINTR ) || ( errno == EAGAIN ) ) + return( READ_EAGAIN ); +#ifdef HAVE_LIBSSL + if ( m_bssl ) + { + int iErr = SSL_get_error( m_ssl, bytes ); + if ( ( iErr != SSL_ERROR_WANT_READ ) && ( iErr != SSL_ERROR_WANT_WRITE ) ) + return( READ_ERR ); + else + return( READ_EAGAIN ); + } +#else + return( READ_ERR ); +#endif /* HAVE_LIBSSL */ + } + + m_iBytesRead += (unsigned long long)bytes; + + return( bytes ); + } + + CS_STRING GetLocalIP() + { + if ( !m_sLocalIP.empty() ) + return( m_sLocalIP ); + + int iSock = GetSock(); + + if ( iSock < 0 ) + return( "" ); + + struct sockaddr_in mLocalAddr; + socklen_t mLocalLen = sizeof(struct sockaddr); + if ( getsockname( iSock, (struct sockaddr *) &mLocalAddr, &mLocalLen ) == 0 ) + m_sLocalIP = inet_ntoa( mLocalAddr.sin_addr ); + + return( m_sLocalIP ); + } + + CS_STRING GetRemoteIP() + { + if ( !m_sRemoteIP.empty() ) + return( m_sRemoteIP ); + + int iSock = GetSock(); + + if ( iSock <= 0 ) + { + cerr << "What the hell is wrong with my fd!?" << endl; + return( "" ); + } + + struct sockaddr_in mRemoteAddr; + socklen_t mRemoteLen = sizeof(struct sockaddr); + if ( getpeername( iSock, (struct sockaddr *) &mRemoteAddr, &mRemoteLen ) == 0 ) + m_sRemoteIP = inet_ntoa( mRemoteAddr.sin_addr ); + + return( m_sRemoteIP ); + } + + //! Tells you if the socket is connected + virtual bool IsConnected() { return( m_bIsConnected ); } + //! Sets the sock, telling it its connected (internal use only) + virtual void SetIsConnected( bool b ) { m_bIsConnected = b; } + + //! returns a reference to the sock + int & GetRSock() { return( m_iReadSock ); } + void SetRSock( int iSock ) { m_iReadSock = iSock; } + int & GetWSock() { return( m_iWriteSock ); } + void SetWSock( int iSock ) { m_iWriteSock = iSock; } + + void SetSock( int iSock ) { m_iWriteSock = iSock; m_iReadSock = iSock; } + int & GetSock() { return( m_iReadSock ); } + + //! resets the time counter + void ResetTimer() { m_iTcount = 0; } + + /** + * this timeout isn't just connection timeout, but also timeout on + * NOT recieving data, to disable this set it to 0 + * then the normal TCP timeout will apply (basically TCP will kill a dead connection) + * Set the timeout, set to 0 to never timeout + */ + void SetTimeout( int iTimeout ) { m_itimeout = iTimeout; } + int GetTimeout() const { return m_itimeout; } + + //! returns true if the socket has timed out + virtual bool CheckTimeout() + { + if ( m_itimeout > 0 ) + { + if ( m_iTcount >= m_itimeout ) + { + Timeout(); + return( true ); + } + + m_iTcount++; + } + return( false ); + } + + /** + * pushes data up on the buffer, if a line is ready + * it calls the ReadLine event + */ + virtual void PushBuff( const char *data, int len ) + { + if ( !m_bEnableReadLine ) + return; // If the ReadLine event is disabled, just ditch here + + if ( data ) + m_sbuffer.append( data, len ); + + while( true ) + { + u_int iFind = m_sbuffer.find( "\n" ); + + if ( iFind != CS_STRING::npos ) + { + CS_STRING sBuff = m_sbuffer.substr( 0, iFind + 1 ); // read up to(including) the newline + m_sbuffer.erase( 0, iFind + 1 ); // erase past the newline + ReadLine( sBuff ); + + } else + break; + } + + if ( ( m_iMaxStoredBufferLength > 0 ) && ( m_sbuffer.length() > m_iMaxStoredBufferLength ) ) + ReachedMaxBuffer(); // call the max read buffer event + + } + + //! This gives access to the internal buffer, if your + //! not going to use GetLine(), then you may want to clear this out + //! (if its binary data and not many '\n' + CS_STRING & GetInternalBuffer() { return( m_sbuffer ); } + void SetMaxBufferThreshold( u_int iThreshold ) { m_iMaxStoredBufferLength = iThreshold; } + u_int GetMaxBufferThreshold() { return( m_iMaxStoredBufferLength ); } + + //! Returns the connection type from enum eConnType + int GetType() { return( m_iConnType ); } + void SetType( int iType ) { m_iConnType = iType; } + + //! Returns a reference to the socket name + const CS_STRING & GetSockName() { return( m_sSockName ); } + void SetSockName( const CS_STRING & sName ) { m_sSockName = sName; } + + //! Returns a reference to the host name + const CS_STRING & GetHostName() { return( m_shostname ); } + void SetHostName( const CS_STRING & sHostname ) { m_shostname = sHostname; } + + + //! Gets the starting time of this socket + unsigned long long GetStartTime() const { return( m_iStartTime ); } + //! Resets the start time + void ResetStartTime() { m_iStartTime = 0; } + + //! Gets the amount of data read during the existence of the socket + unsigned long long GetBytesRead() const { return( m_iBytesRead ); } + void ResetBytesRead() { m_iBytesRead = 0; } + + //! Gets the amount of data written during the existence of the socket + unsigned long long GetBytesWritten() const { return( m_iBytesWritten ); } + void ResetBytesWritten() { m_iBytesWritten = 0; } + + + //! Get Avg Read Speed in sample milliseconds (default is 1000 milliseconds or 1 second) + double GetAvgRead( unsigned long long iSample = 1000 ) + { + unsigned long long iDifference = ( millitime() - m_iStartTime ); + + if ( ( m_iBytesRead == 0 ) || ( iSample > iDifference ) ) + return( (double)m_iBytesRead ); + + return( ( (double)m_iBytesRead / ( (double)iDifference / (double)iSample ) ) ); + } + + //! Get Avg Write Speed in sample milliseconds (default is 1000 milliseconds or 1 second) + double GetAvgWrite( unsigned long long iSample = 1000 ) + { + unsigned long long iDifference = ( millitime() - m_iStartTime ); + + if ( ( m_iBytesWritten == 0 ) || ( iSample > iDifference ) ) + return( (double)m_iBytesRead ); + + return( ( (double)m_iBytesWritten / ( (double)iDifference / (double)iSample ) ) ); + } + + + //! Returns the remote port + int GetRemotePort() + { + if ( m_iRemotePort > 0 ) + return( m_iRemotePort ); + + int iSock = GetSock(); + + if ( iSock >= 0 ) + { + struct sockaddr_in mAddr; + socklen_t mLen = sizeof(struct sockaddr); + if ( getpeername( iSock, (struct sockaddr *) &mAddr, &mLen ) == 0 ) + m_iRemotePort = ntohs( mAddr.sin_port ); + } + + return( m_iRemotePort ); + } + + //! Returns the local port + int GetLocalPort() + { + if ( m_iLocalPort > 0 ) + return( m_iLocalPort ); + + int iSock = GetSock(); + + if ( iSock >= 0 ) + { + struct sockaddr_in mLocalAddr; + socklen_t mLocalLen = sizeof(struct sockaddr); + if ( getsockname( iSock, (struct sockaddr *) &mLocalAddr, &mLocalLen ) == 0 ) + m_iLocalPort = ntohs( mLocalAddr.sin_port ); + } + + return( m_iLocalPort ); + } + + //! Returns the port + int GetPort() { return( m_iport ); } + void SetPort( int iPort ) { m_iport = iPort; } + + //! just mark us as closed, the parent can pick it up + void Close() { m_bClosed = true; } + //! returns true if the socket is closed + bool isClosed() { return( m_bClosed ); } + + //! Set rather to NON Blocking IO on this socket, default is true + void BlockIO( bool bBLOCK ) { m_bBLOCK = bBLOCK; } + + //! Use this to change your fd's to blocking or none blocking + void NonBlockingIO() + { + int fdflags = fcntl ( m_iReadSock, F_GETFL, 0); + fcntl( m_iReadSock, F_SETFL, fdflags|O_NONBLOCK ); + + if ( m_iReadSock != m_iWriteSock ) + { + fdflags = fcntl ( m_iWriteSock, F_GETFL, 0); + fcntl( m_iWriteSock, F_SETFL, fdflags|O_NONBLOCK ); + } + + BlockIO( false ); + } + + //! if this connection type is ssl or not + bool GetSSL() { return( m_bssl ); } + void SetSSL( bool b ) { m_bssl = b; } + +#ifdef HAVE_LIBSSL + //! Set the cipher type ( openssl cipher [to see ciphers available] ) + void SetCipher( const CS_STRING & sCipher ) { m_sCipherType = sCipher; } + const CS_STRING & GetCipher() { return( m_sCipherType ); } + + //! Set the pem file location + void SetPemLocation( const CS_STRING & sPemFile ) { m_sPemFile = sPemFile; } + const CS_STRING & GetPemLocation() { return( m_sPemFile ); } + void SetPemPass( const CS_STRING & sPassword ) { m_sPemPass = sPassword; } + const CS_STRING & GetPemPass() const { return( m_sPemPass ); } + static int PemPassCB( char *buf, int size, int rwflag, void *pcSocket ) + { + Csock *pSock = (Csock *)pcSocket; + const CS_STRING & sPassword = pSock->GetPemPass(); + memset( buf, '\0', size ); + strncpy( buf, sPassword.c_str(), size ); + buf[size-1] = '\0'; + return( strlen( buf ) ); + } + static int CertVerifyCB( int preverify_ok, X509_STORE_CTX *x509_ctx ) + { + /* return 1 always for now, probably want to add some code for cert verification */ + return( 1 ); + } + + //! Set the SSL method type + void SetSSLMethod( int iMethod ) { m_iMethod = iMethod; } + int GetSSLMethod() { return( m_iMethod ); } + + void SetSSLObject( SSL *ssl ) { m_ssl = ssl; } + void SetCTXObject( SSL_CTX *sslCtx ) { m_ssl_ctx = sslCtx; } + void SetFullSSLAccept() { m_bFullsslAccept = true; } + SSL_SESSION * GetSSLSession() { return( SSL_get_session( m_ssl ) ); } +#endif /* HAVE_LIBSSL */ + + //! Get the send buffer + const CS_STRING & GetSendBuff() { return( m_sSend ); } + + //! is SSL_accept finished ? + bool FullSSLAccept() { return ( m_bFullsslAccept ); } + //! is the ssl properly finished (from write no error) + bool SslIsEstablished() { return ( m_bsslEstablished ); } + + //! returns the underlying buffer + CS_STRING & GetBuffer() { return( m_sSend ); } + + //! Use this to bind this socket to inetd + bool ConnectInetd( bool bIsSSL = false, const CS_STRING & sHostname = "" ) + { + if ( !sHostname.empty() ) + m_sSockName = sHostname; + + // set our hostname + if ( m_sSockName.empty() ) + { + struct sockaddr_in client; + socklen_t clen = sizeof( client ); + if ( getpeername( 0, (struct sockaddr *)&client, &clen ) < 0 ) + m_sSockName = "0.0.0.0:0"; + else + { + stringstream s; + s << inet_ntoa( client.sin_addr ) << ":" << ntohs( client.sin_port ); + m_sSockName = s.str(); + } + } + + return( ConnectFD( 0, 1, m_sSockName, bIsSSL, INBOUND ) ); + } + + //! Tie this guy to an existing real file descriptor + bool ConnectFD( int iReadFD, int iWriteFD, const CS_STRING & sName, bool bIsSSL = false, ETConn eDirection = INBOUND ) + { + if ( eDirection == LISTENER ) + { + CS_DEBUG( "You can not use a LISTENER type here!" ); + return( false ); + } + + // set our socket type + SetType( eDirection ); + + // set the hostname + m_sSockName = sName; + + // set the file descriptors + SetRSock( iReadFD ); + SetWSock( iWriteFD ); + + // set it up as non-blocking io + NonBlockingIO(); + + if ( bIsSSL ) + { + if ( ( eDirection == INBOUND ) && ( !AcceptSSL() ) ) + return( false ); + else if ( ( eDirection == OUTBOUND ) && ( !ConnectSSL() ) ) + return( false ); + } + + return( true ); + } + + //! Get the peer's X509 cert +#ifdef HAVE_LIBSSL + X509 *getX509() + { + if ( m_ssl ) + return( SSL_get_peer_certificate( m_ssl ) ); + + return( NULL ); + } + + //! + //! Returns The Peers Public Key + //! + CS_STRING GetPeerPubKey() + { + CS_STRING sKey; + + SSL_SESSION *pSession = GetSSLSession(); + + if ( ( pSession ) && ( pSession->peer ) ) + { + EVP_PKEY *pKey = X509_get_pubkey( pSession->peer ); + if ( pKey ) + { + char *hxKey = NULL; + switch( pKey->type ) + { + case EVP_PKEY_RSA: + { + hxKey = BN_bn2hex( pKey->pkey.rsa->n ); + break; + } + case EVP_PKEY_DSA: + { + hxKey = BN_bn2hex( pKey->pkey.dsa->pub_key ); + break; + } + default: + { + CS_DEBUG( "Not Prepared for Public Key Type [" << pKey->type << "]" ); + break; + } + } + if ( hxKey ) + { + sKey = hxKey; + OPENSSL_free( hxKey ); + } + EVP_PKEY_free( pKey ); + } + } + return( sKey ); + } + bool RequiresClientCert() { return( m_bRequireClientCert ); } + void SetRequiresClientCert( bool bRequiresCert ) { m_bRequireClientCert = bRequiresCert; } + +#endif /* HAVE_LIBSSL */ + + //! Set The INBOUND Parent sockname + virtual void SetParentSockName( const CS_STRING & sParentName ) { m_sParentName = sParentName; } + const CS_STRING & GetParentSockName() { return( m_sParentName ); } + + /* + * sets the rate at which we can send data + * \param iBytes the amount of bytes we can write + * \param iMilliseconds the amount of time we have to rate to iBytes + */ + virtual void SetRate( u_int iBytes, unsigned long long iMilliseconds ) + { + m_iMaxBytes = iBytes; + m_iMaxMilliSeconds = iMilliseconds; + } + + u_int GetRateBytes() { return( m_iMaxBytes ); } + unsigned long long GetRateTime() { return( m_iMaxMilliSeconds ); } + + + //! This has a garbage collecter, and is used internall to call the jobs + virtual void Cron() + { + for( unsigned int a = 0; a < m_vcCrons.size(); a++ ) + { + CCron *pcCron = m_vcCrons[a]; + + if ( !pcCron->isValid() ) + { + CS_Delete( pcCron ); + m_vcCrons.erase( m_vcCrons.begin() + a-- ); + } else + pcCron->run(); + } + } + + //! insert a newly created cron + virtual void AddCron( CCron * pcCron ) + { + m_vcCrons.push_back( pcCron ); + } + + //! delete cron(s) by name + virtual void DelCron( const CS_STRING & sName, bool bDeleteAll = true, bool bCaseSensitive = true ) + { + for( u_int a = 0; a < m_vcCrons.size(); a++ ) + { + int (*Cmp)(const char *, const char *) = ( bCaseSensitive ? strcmp : strcasecmp ); + if ( Cmp( m_vcCrons[a]->GetName().c_str(), sName.c_str() ) == 0 ) + { + m_vcCrons[a]->Stop(); + CS_Delete( m_vcCrons[a] ); + m_vcCrons.erase( m_vcCrons.begin() + a-- ); + } + } + } + + //! delete cron by idx + virtual void DelCron( u_int iPos ) + { + if ( iPos < m_vcCrons.size() ) + { + m_vcCrons[iPos]->Stop(); + CS_Delete( m_vcCrons[iPos] ); + m_vcCrons.erase( m_vcCrons.begin() + iPos ); + } + } + //! delete cron by address + virtual void DelCronByAddr( CCron *pcCron ) + { + for( u_int a = 0; a < m_vcCrons.size(); a++ ) + { + if ( m_vcCrons[a] == pcCron ) + { + m_vcCrons[a]->Stop(); + CS_Delete( m_vcCrons[a] ); + m_vcCrons.erase( m_vcCrons.begin() + a ); + return; + } + } + } + + /** + * Override these functions for an easy interface when using the Socket Manager + * Don't bother using these callbacks if you are using this class directly (without Socket Manager) + * as the Socket Manager calls most of these callbacks + * + * Connected event + */ + virtual void Connected() {} + /** + * Override these functions for an easy interface when using the Socket Manager + * Don't bother using these callbacks if you are using this class directly (without Socket Manager) + * as the Socket Manager calls most of these callbacks + * + * Disconnected event + */ + virtual void Disconnected() {} + /** + * Override these functions for an easy interface when using the Socket Manager + * Don't bother using these callbacks if you are using this class directly (without Socket Manager) + * as the Socket Manager calls most of these callbacks + * + * Sock Timed out event + */ + virtual void Timeout() {} + /** + * Override these functions for an easy interface when using the Socket Manager + * Don't bother using these callbacks if you are using this class directly (without Socket Manager) + * as the Socket Manager calls most of these callbacks + * + * Ready to read data event + */ + virtual void ReadData( const char *data, int len ) {} + /** + * Override these functions for an easy interface when using the Socket Manager + * Don't bother using these callbacks if you are using this class directly (without Socket Manager) + * as the Socket Manager calls most of these callbacks. + * With ReadLine, if your not going to use it IE a data stream, @see EnableReadLine() + * + * Ready to Read a full line event + */ + virtual void ReadLine( const CS_STRING & sLine ) {} + //! set the value of m_bEnableReadLine to true, we don't want to store a buffer for ReadLine, unless we want it + void EnableReadLine() { m_bEnableReadLine = true; } + void DisableReadLine() { m_bEnableReadLine = false; } + + /** + * Override these functions for an easy interface when using the Socket Manager + * Don't bother using these callbacks if you are using this class directly (without Socket Manager) + * as the Socket Manager calls most of these callbacks + * This WARNING event is called when your buffer for readline exceeds the warning threshold + * and triggers this event. Either Override it and do nothing, or @SetMaxBufferThreshold( int ) + * This event will only get called if m_bEnableReadLine is enabled + */ + virtual void ReachedMaxBuffer() + { + cerr << "Warning, Max Buffer length Warning Threshold has been hit" << endl; + cerr << "If you don't care, then set SetMaxBufferThreshold to 0" << endl; + } + /** + * Override these functions for an easy interface when using the Socket Manager + * Don't bother using these callbacks if you are using this class directly (without Socket Manager) + * as the Socket Manager calls most of these callbacks + * + * A sock error occured event + */ + virtual void SockError( int iErrno ) {} + /** + * Override these functions for an easy interface when using the Socket Manager + * Don't bother using these callbacks if you are using this class directly (without Socket Manager) + * as the Socket Manager calls most of these callbacks + * + * + * Incoming Connection Event + * return false and the connection will fail + * default returns true + */ + virtual bool ConnectionFrom( const CS_STRING & sHost, int iPort ) { return( true ); } + + /** + * Override these functions for an easy interface when using the Socket Manager + * Don't bother using these callbacks if you are using this class directly (without Socket Manager) + * as the Socket Manager calls most of these callbacks + * + * Connection Refused Event + * + */ + virtual void ConnectionRefused() {} + + /** + * This is ONLY called on the LISTENER, returning false from this + * will cause the LISTENER to ABORT on the connection and disregard the + * object. + */ + virtual bool CreatedChild( Csock *pSock ) { return( true ); } + //! return the data imediatly ready for read + virtual int GetPending() + { +#ifdef HAVE_LIBSSL + if( m_ssl ) + return( SSL_pending( m_ssl ) ); + else + return( 0 ); +#else + return( 0 ); +#endif /* HAVE_LIBSSL */ + } + + ////////////////////////////////////////////////// + + private: + int m_iReadSock, m_iWriteSock, m_itimeout, m_iport, m_iConnType, m_iTcount, m_iMethod, m_iRemotePort, m_iLocalPort; + bool m_bssl, m_bIsConnected, m_bClosed, m_bBLOCK, m_bFullsslAccept; + bool m_bsslEstablished, m_bEnableReadLine, m_bRequireClientCert; + CS_STRING m_shostname, m_sbuffer, m_sSockName, m_sPemFile, m_sCipherType, m_sParentName; + CS_STRING m_sSend, m_sSSLBuffer, m_sPemPass, m_sLocalIP, m_sRemoteIP; + + unsigned long long m_iMaxMilliSeconds, m_iLastSendTime, m_iBytesRead, m_iBytesWritten, m_iStartTime; + unsigned int m_iMaxBytes, m_iLastSend, m_iMaxStoredBufferLength; + + struct sockaddr_in m_address; + +#ifdef HAVE_LIBSSL + SSL *m_ssl; + SSL_CTX *m_ssl_ctx; + SSL_METHOD *m_ssl_method; + + virtual void FREE_SSL() + { + if ( m_ssl ) + { + SSL_shutdown( m_ssl ); + SSL_free( m_ssl ); + } + m_ssl = NULL; + } + + virtual void FREE_CTX() + { + if ( m_ssl_ctx ) + SSL_CTX_free( m_ssl_ctx ); + + m_ssl_ctx = NULL; + } + +#endif /* HAVE_LIBSSL */ + + vector m_vcCrons; + + //! Create the socket + virtual int SOCKET( bool bListen = false ) + { + int iRet = socket( PF_INET, SOCK_STREAM, IPPROTO_TCP ); + + if ( ( iRet > -1 ) && ( bListen ) ) + { + const int on = 1; + + if ( setsockopt( iRet, SOL_SOCKET, SO_REUSEADDR, &on, sizeof( on ) ) != 0 ) + PERROR( "setsockopt" ); + + } else if ( iRet == -1 ) + PERROR( "socket" ); + + return( iRet ); + } + + virtual void Init( const CS_STRING & sHostname, int iport, int itimeout = 60 ) + { +#ifdef HAVE_LIBSSL + m_ssl = NULL; + m_ssl_ctx = NULL; +#endif /* HAVE_LIBSSL */ + m_iReadSock = -1; + m_iWriteSock = -1; + m_itimeout = itimeout; + m_bssl = false; + m_bIsConnected = false; + m_iport = iport; + m_shostname = sHostname; + m_iTcount = 0; + m_sbuffer.clear(); + m_bClosed = false; + m_bBLOCK = true; + m_iMethod = SSL23; + m_sCipherType = "ALL"; + m_iMaxBytes = 0; + m_iMaxMilliSeconds = 0; + m_iLastSendTime = 0; + m_iLastSend = 0; + m_bFullsslAccept = false; + m_bsslEstablished = false; + m_bEnableReadLine = false; + m_bRequireClientCert = false; + m_iMaxStoredBufferLength = 1024; + m_iConnType = INBOUND; + m_iRemotePort = 0; + m_iLocalPort = 0; + m_iBytesRead = 0; + m_iBytesWritten = 0; + m_iStartTime = millitime(); + } + }; + + /** + * @class TSocketManager + * @brief Best class to use to interact with the sockets + * + * handles SSL and NON Blocking IO + * Its a template class since Csock derives need to be new'd correctly + * Makes it easier to use overall + * Rather then use it directly, you'll probably get more use deriving from it + * Another thing to note, is that all sockets are deleted implicitly, so obviously you + * cant pass in Csock classes created on the stack. For those of you who don't + * know STL very well, the reason I did this is because whenever you add to certain stl containers + * (ie vector, or map), its completely rebuilt using the copy constructor on each element. + * That then means the constructor and destructor are called on every item in the container. + * Not only is this more overhead then just moving pointers around, its dangerous as if you have + * an object that is newed and deleted in the destructor the value of its pointer is copied in the + * default copy constructor. This means everyone has to know better and create a copy constructor, + * or I just make everyone new their object :) + * + * class CBlahSock : public TSocketManager + * + * @author Jim Hull + */ + + template + class TSocketManager : public vector + { + public: + TSocketManager() : vector() + { + m_errno = SUCCESS; + m_iCallTimeouts = millitime(); + m_iSelectWait = 100000; // Default of 100 milliseconds + } + + virtual ~TSocketManager() + { + Cleanup(); + } + + void clear() + { + for( unsigned int i = 0; i < size(); i++ ) + CS_Delete( (*this)[i] ); + + vector::clear(); + } + + virtual void Cleanup() + { + for( u_int a = 0; a < m_vcCrons.size(); a++ ) + CS_Delete( m_vcCrons[a] ); + + m_vcCrons.clear(); + clear(); + } + + enum EMessages + { + SUCCESS = 0, //! Select returned more then 1 fd ready for action + SELECT_ERROR = -1, //! An Error Happened, Probably dead socket. That socket is returned if available + SELECT_TIMEOUT = -2, //! Select Timeout + SELECT_TRYAGAIN = -3 //! Select calls for you to try again + + }; + + /** + * Create a connection + * + * \param sHostname the destination + * \param iPort the destination port + * \param sSockName the Socket Name ( should be unique ) + * \param iTimeout the amount of time to try to connect + * \param isSSL does the connection require a SSL layer + * \param sBindHost the host to bind too + * \return true on success + */ + virtual bool Connect( const CS_STRING & sHostname, int iPort , const CS_STRING & sSockName, int iTimeout = 60, bool isSSL = false, const CS_STRING & sBindHost = "", T *pcSock = NULL ) + { + // create the new object + if ( !pcSock ) + pcSock = new T( sHostname, iPort, iTimeout ); + else + { + pcSock->SetHostName( sHostname ); + pcSock->SetPort( iPort ); + pcSock->SetTimeout( iTimeout ); + } + + // make it NON-Blocking IO + pcSock->BlockIO( false ); + + if ( !pcSock->Connect( sBindHost ) ) + { + if ( errno == ECONNREFUSED ) + pcSock->ConnectionRefused(); + + CS_Delete( pcSock ); + return( false ); + } + +#ifdef HAVE_LIBSSL + if ( isSSL ) + { + if ( !pcSock->ConnectSSL() ) + { + if ( errno == ECONNREFUSED ) + pcSock->ConnectionRefused(); + + CS_Delete( pcSock ); + return( false ); + } + } +#endif /* HAVE_LIBSSL */ + + AddSock( pcSock, sSockName ); + return( true ); + } + + /** + * Create a listening socket + * + * \param iPort the port to listen on + * \param sSockName the name of the socket + * \param isSSL if the sockets created require an ssl layer + * \param iMaxConns the maximum amount of connections to accept + * \return true on success + */ + virtual T * ListenHost( int iPort, const CS_STRING & sSockName, const CS_STRING & sBindHost, int isSSL = false, int iMaxConns = SOMAXCONN, T *pcSock = NULL, u_int iTimeout = 0 ) + { + if ( !pcSock ) + pcSock = new T(); + + pcSock->BlockIO( false ); + + pcSock->SetSSL( isSSL ); + + if ( pcSock->Listen( iPort, iMaxConns, sBindHost, iTimeout ) ) + { + AddSock( pcSock, sSockName ); + return( pcSock ); + } + CS_Delete( pcSock ); + return( NULL ); + } + + virtual bool ListenAll( int iPort, const CS_STRING & sSockName, int isSSL = false, int iMaxConns = SOMAXCONN, T *pcSock = NULL, u_int iTimeout = 0 ) + { + return( ListenHost( iPort, sSockName, "", isSSL, iMaxConns, pcSock, iTimeout ) ); + } + + /* + * @return the port number being listened on + */ + virtual u_short ListenRand( const CS_STRING & sSockName, const CS_STRING & sBindHost, int isSSL = false, int iMaxConns = SOMAXCONN, T *pcSock = NULL, u_int iTimeout = 0 ) + { + u_short iPort = 0; + T *pNewSock = ListenHost( 0, sSockName, sBindHost, isSSL, iMaxConns, pcSock, iTimeout ); + if ( pNewSock ) + { + int iSock = pNewSock->GetSock(); + + if ( iSock < 0 ) + { + CS_DEBUG( "Failed to attain a valid file descriptor" ); + pNewSock->Close(); + return( 0 ); + } + + struct sockaddr_in mLocalAddr; + socklen_t mLocalLen = sizeof(struct sockaddr); + getsockname( iSock, (struct sockaddr *) &mLocalAddr, &mLocalLen ); + + iPort = ntohs( mLocalAddr.sin_port ); + } + + return( iPort ); + } + virtual u_short ListenAllRand( const CS_STRING & sSockName, int isSSL = false, int iMaxConns = SOMAXCONN, T *pcSock = NULL, u_int iTimeout = 0 ) + { + return( ListenRand( sSockName, "", isSSL, iMaxConns, pcSock, iTimeout ) ); + } + + /* + * Best place to call this class for running, all the call backs are called + * You should through this in your main while loop (long as its not blocking) + * all the events are called as needed + */ + virtual void Loop () + { + map mpeSocks; + Select( mpeSocks ); + set spReadySocks; + + switch( m_errno ) + { + case SUCCESS: + { + for( typename map::iterator itSock = mpeSocks.begin(); itSock != mpeSocks.end(); itSock++ ) + { + T * pcSock = itSock->first; + EMessages iErrno = itSock->second; + + // mark that this sock was ready + spReadySocks.insert( pcSock ); + if ( iErrno == SUCCESS ) + { + pcSock->ResetTimer(); // reset the timeout timer + + // read in data + // if this is a + char *buff; + int iLen = 0; + + if ( pcSock->GetSSL() ) + iLen = pcSock->GetPending(); + + if ( iLen > 0 ) + { + buff = (char *)malloc( iLen ); + } else + { + iLen = CS_BLOCKSIZE; + buff = (char *)malloc( CS_BLOCKSIZE ); + + } + + int bytes = pcSock->Read( buff, iLen ); + + if ( ( bytes != T::READ_CONNREFUSED ) && ( !pcSock->IsConnected() ) ) + { + pcSock->SetIsConnected( true ); + pcSock->Connected(); + } + + switch( bytes ) + { + case T::READ_EOF: + { + DelSockByAddr( pcSock ); + break; + } + + case T::READ_ERR: + { + pcSock->SockError( errno ); + DelSockByAddr( pcSock ); + break; + } + + case T::READ_EAGAIN: + break; + + case T::READ_CONNREFUSED: + pcSock->ConnectionRefused(); + DelSockByAddr( pcSock ); + break; + + default: + { + pcSock->PushBuff( buff, bytes ); + pcSock->ReadData( buff, bytes ); + break; + } + } + // free up the buff + free( buff ); + + } else if ( iErrno == SELECT_ERROR ) + { + // a socket came back with an error + // usually means it was closed + DelSockByAddr( pcSock ); + } + } + break; + } + + case SELECT_TIMEOUT: + case SELECT_ERROR: + default : + break; + } + + unsigned long long iMilliNow = millitime(); + if ( ( iMilliNow - m_iCallTimeouts ) > 1000 ) + { + m_iCallTimeouts = iMilliNow; + // call timeout on all the sockets that recieved no data + for( unsigned int i = 0; i < size(); i++ ) + { + if ( (*this)[i]->GetType() != T::LISTENER ) + { + // are we in the map of found socks ? + if ( spReadySocks.find( (*this)[i] ) == spReadySocks.end() ) + { + if ( (*this)[i]->CheckTimeout() ) + DelSock( i-- ); + } + } else + { + if ( (*this)[i]->CheckTimeout() ) + DelSock( i-- ); + } + } + } + // run any Manager Crons we may have + Cron(); + } + + /* + * Make this method virtual, so you can override it when a socket is added + * Assuming you might want to do some extra stuff + */ + virtual void AddSock( T *pcSock, const CS_STRING & sSockName ) + { + pcSock->SetSockName( sSockName ); + push_back( pcSock ); + } + + //! returns a pointer to the FIRST sock found by port or NULL on no match + virtual T * FindSockByRemotePort( int iPort ) + { + for( unsigned int i = 0; i < size(); i++ ) + { + if ( (*this)[i]->GetRemotePort() == iPort ) + return( (*this)[i] ); + } + + return( NULL ); + } + + //! returns a pointer to the FIRST sock found by port or NULL on no match + virtual T * FindSockByLocalPort( int iPort ) + { + for( unsigned int i = 0; i < size(); i++ ) + if ( (*this)[i]->GetLocalPort() == iPort ) + return( (*this)[i] ); + + return( NULL ); + } + + //! returns a pointer to the FIRST sock found by name or NULL on no match + virtual T * FindSockByName( const CS_STRING & sName ) + { + for( unsigned int i = 0; i < size(); i++ ) + if ( (*this)[i]->GetSockName() == sName ) + return( (*this)[i] ); + + return( NULL ); + } + + virtual vector FindSocksByName( const CS_STRING & sName ) + { + vector vpSocks; + + for( unsigned int i = 0; i < size(); i++ ) + if ( (*this)[i]->GetSockName() == sName ) + vpSocks.push_back( (*this)[i] ); + + return( vpSocks ); + } + + //! returns a vector of pointers to socks with sHostname as being connected + virtual vector FindSocksByRemoteHost( const CS_STRING & sHostname ) + { + vector vpSocks; + + for( unsigned int i = 0; i < size(); i++ ) + if ( (*this)[i]->GetHostName() == sHostname ) + vpSocks.push_back( (*this)[i] ); + + return( vpSocks ); + } + + + //! return the last known error as set by this class + int GetErrno() { return( m_errno ); } + + //! add a cronjob at the manager level + virtual void AddCron( CCron *pcCron ) + { + m_vcCrons.push_back( pcCron ); + } + + //! delete cron(s) by name + virtual void DelCron( const CS_STRING & sName, bool bDeleteAll = true, bool bCaseSensitive = true ) + { + for( u_int a = 0; a < m_vcCrons.size(); a++ ) + { + int (*Cmp)(const char *, const char *) = ( bCaseSensitive ? strcmp : strcasecmp ); + if ( Cmp( m_vcCrons[a]->GetName().c_str(), sName.c_str() ) == 0 ) + { + m_vcCrons[a]->Stop(); + CS_Delete( m_vcCrons[a] ); + m_vcCrons.erase( m_vcCrons.begin() + a-- ); + } + } + } + + //! delete cron by idx + virtual void DelCron( u_int iPos ) + { + if ( iPos < m_vcCrons.size() ) + { + m_vcCrons[iPos]->Stop(); + CS_Delete( m_vcCrons[iPos] ); + m_vcCrons.erase( m_vcCrons.begin() + iPos ); + } + } + //! delete cron by address + virtual void DelCronByAddr( CCron *pcCron ) + { + for( u_int a = 0; a < m_vcCrons.size(); a++ ) + { + if ( m_vcCrons[a] == pcCron ) + { + m_vcCrons[a]->Stop(); + CS_Delete( m_vcCrons[a] ); + m_vcCrons.erase( m_vcCrons.begin() + a ); + return; + } + } + } + + //! Get the Select Timeout in MILLISECONDS + u_int GetSelectTimeout() { return( m_iSelectWait ); } + //! Set the Select Timeout in MILLISECONDS + void SetSelectTimeout( u_int iTimeout ) { m_iSelectWait = iTimeout; } + + vector & GetCrons() { return( m_vcCrons ); } + + //! Delete a sock by addr + //! its position is looked up + //! the socket is deleted, the appropriate call backs are peformed + //! and its instance is removed from the manager + virtual void DelSockByAddr( T *pcSock ) + { + for( u_int a = 0; a < size(); a++ ) + { + if ( pcSock == (*this)[a] ) + { + DelSock( a ); + return; + } + } + } + //! Delete a sock by position in the vector + //! the socket is deleted, the appropriate call backs are peformed + //! and its instance is removed from the manager + //! deleting in a loop can be tricky, be sure you watch your position. + //! ie for( u_int a = 0; a < size(); a++ ) DelSock( a-- ); + virtual void DelSock( u_int iPos ) + { + if ( iPos >= size() ) + { + CS_DEBUG( "Invalid Sock Position Requested! [" << iPos << "]" ); + return; + } + if ( (*this)[iPos]->IsConnected() ) + (*this)[iPos]->Disconnected(); // only call disconnected event if connected event was called (IE IsConnected was set) + + CS_Delete( (*this)[iPos] ); + erase( begin() + iPos ); + } + + private: + /** + * fills a map of socks to a message for check + * map is empty if none are ready, check GetErrno() for the error, if not SUCCESS Select() failed + * each struct contains the socks error + * @see GetErrno() + */ + virtual void Select( map & mpeSocks ) + { + mpeSocks.clear(); + struct timeval tv; + fd_set rfds, wfds; + + tv.tv_sec = 0; + tv.tv_usec = m_iSelectWait; + + TFD_ZERO( &rfds ); + TFD_ZERO( &wfds ); + + // before we go any further, Process work needing to be done on the job + for( unsigned int i = 0; i < size(); i++ ) + { + if ( (*this)[i]->isClosed() ) + DelSock( i-- ); // close any socks that have requested it + else + (*this)[i]->Cron(); // call the Cron handler here + } + + bool bHasWriteable = false; + + for( unsigned int i = 0; i < size(); i++ ) + { + + T *pcSock = (*this)[i]; + + if ( pcSock->GetType() != T::LISTENER ) + { + int & iRSock = pcSock->GetRSock(); + int & iWSock = pcSock->GetWSock(); + + if ( ( pcSock->GetSSL() ) && ( pcSock->GetType() == T::INBOUND ) && ( !pcSock->FullSSLAccept() ) ) + { + tv.tv_usec = 1000; // just make sure this returns quick incase we still need pending + // try accept on this socket again + if ( !pcSock->AcceptSSL() ) + pcSock->Close(); + + } else if ( ( pcSock->IsConnected() ) && ( pcSock->GetSendBuff().empty() ) ) + { + TFD_SET( iRSock, &rfds ); + + } else if ( ( pcSock->GetSSL() ) && ( !pcSock->SslIsEstablished() ) && ( !pcSock->GetSendBuff().empty() ) ) + { + // do this here, cause otherwise ssl will cause a small + // cpu spike waiting for the handshake to finish + TFD_SET( iRSock, &rfds ); + // resend this data + if ( !pcSock->Write( "" ) ) + { + pcSock->Close(); + } + + } else + { + TFD_SET( iRSock, &rfds ); + TFD_SET( iWSock, &wfds ); + bHasWriteable = true; + } + + } else + TFD_SET( pcSock->GetRSock(), &rfds ); + } + + // first check to see if any ssl sockets are ready for immediate read + // a mini select() type deal for ssl + for( unsigned int i = 0; i < size(); i++ ) + { + T *pcSock = (*this)[i]; + + if ( ( pcSock->GetSSL() ) && ( pcSock->GetType() != Csock::LISTENER ) ) + { + if ( pcSock->GetPending() > 0 ) + SelectSock( mpeSocks, SUCCESS, pcSock ); + } + } + + // old fashion select, go fer it + int iSel; + + if ( !mpeSocks.empty() ) + tv.tv_usec = 1000; // this won't be a timeout, 1 ms pause to see if anything else is ready (IE if there is SSL data pending, don't wait too long) + + if ( bHasWriteable ) + iSel = select(FD_SETSIZE, &rfds, &wfds, NULL, &tv); + else + iSel = select(FD_SETSIZE, &rfds, NULL, NULL, &tv); + + if ( iSel == 0 ) + { + if ( mpeSocks.empty() ) + m_errno = SELECT_TIMEOUT; + else + m_errno = SUCCESS; + + return; + } + + if ( ( iSel == -1 ) && ( errno == EINTR ) ) + { + if ( mpeSocks.empty() ) + m_errno = SELECT_TRYAGAIN; + else + m_errno = SUCCESS; + + return; + + } else if ( iSel == -1 ) + { + if ( mpeSocks.empty() ) + m_errno = SELECT_ERROR; + else + m_errno = SUCCESS; + + return; + + } else + { + m_errno = SUCCESS; + } + + // find out wich one is ready + for( unsigned int i = 0; i < size(); i++ ) + { + T *pcSock = (*this)[i]; + int & iRSock = pcSock->GetRSock(); + int & iWSock = pcSock->GetWSock(); + EMessages iErrno = SUCCESS; + + if ( TFD_ISSET( iWSock, &wfds ) ) + { + if ( iSel > 0 ) + { + iErrno = SUCCESS; + if ( ( !pcSock->GetSendBuff().empty() ) && ( pcSock->IsConnected() ) ) + { // write whats in the socks send buffer + if ( !pcSock->Write( "" ) ) + { + // write failed, sock died :( + iErrno = SELECT_ERROR; + } + } + } else + iErrno = SELECT_ERROR; + + SelectSock( mpeSocks, iErrno, pcSock ); + + } else if ( TFD_ISSET( iRSock, &rfds ) ) + { + if ( iSel > 0 ) + iErrno = SUCCESS; + else + iErrno = SELECT_ERROR; + + if ( pcSock->GetType() != T::LISTENER ) + SelectSock( mpeSocks, iErrno, pcSock ); + else // someone is coming in! + { + CS_STRING sHost; + int port; + int inSock = pcSock->Accept( sHost, port ); + + if ( inSock != -1 ) + { + pcSock->ResetTimer(); // let them now it got dinged + + // if we have a new sock, then add it + T *NewpcSock = (T *)pcSock->GetSockObj( sHost, port ); + + if ( !NewpcSock ) + NewpcSock = new T( sHost, port ); + + NewpcSock->BlockIO( false ); + + NewpcSock->SetType( T::INBOUND ); + NewpcSock->SetRSock( inSock ); + NewpcSock->SetWSock( inSock ); + + bool bAddSock = true; +#ifdef HAVE_LIBSSL + // + // is this ssl ? + if ( pcSock->GetSSL() ) + { + NewpcSock->SetCipher( pcSock->GetCipher() ); + NewpcSock->SetPemLocation( pcSock->GetPemLocation() ); + NewpcSock->SetPemPass( pcSock->GetPemPass() ); + NewpcSock->SetRequiresClientCert( pcSock->RequiresClientCert() ); + bAddSock = NewpcSock->AcceptSSL(); + } + +#endif /* HAVE_LIBSSL */ + if ( bAddSock ) + { + // set the name of the listener + NewpcSock->SetParentSockName( pcSock->GetSockName() ); + NewpcSock->SetRate( pcSock->GetRateBytes(), pcSock->GetRateTime() ); + + if ( pcSock->CreatedChild( (T *)NewpcSock ) ) + { // last step, notify the listener that this socket was created succesfully + if ( NewpcSock->GetSockName().empty() ) + { + stringstream s; + s << sHost << ":" << port; + AddSock( NewpcSock, s.str() ); + + } else + AddSock( NewpcSock, NewpcSock->GetSockName() ); + } else + CS_Delete( NewpcSock ); + + + } else + CS_Delete( NewpcSock ); + } + } + } + } + } + + + //! internal use only + virtual void SelectSock( map & mpeSocks, EMessages eErrno, T * pcSock ) + { + if ( mpeSocks.find( pcSock ) != mpeSocks.end() ) + return; + + mpeSocks[pcSock] = eErrno; + } + + //! these crons get ran and checked in Loop() + virtual void Cron() + { + for( unsigned int a = 0; a < m_vcCrons.size(); a++ ) + { + CCron *pcCron = m_vcCrons[a]; + + if ( !pcCron->isValid() ) + { + CS_Delete( pcCron ); + m_vcCrons.erase( m_vcCrons.begin() + a-- ); + } else + pcCron->run(); + } + } + + EMessages m_errno; + vector m_vcCrons; + unsigned long long m_iCallTimeouts; + u_int m_iSelectWait; + }; + + //! basic socket class + typedef TSocketManager CSocketManager; + +#ifndef _NO_CSOCKET_NS +}; +#endif /* _NO_CSOCKET_NS */ + +#endif /* _HAS_CSOCKET_ */ + diff --git a/DCCBounce.cpp b/DCCBounce.cpp new file mode 100644 index 00000000..96eb7702 --- /dev/null +++ b/DCCBounce.cpp @@ -0,0 +1,130 @@ +#include "DCCBounce.h" + +void CDCCBounce::ReadLine(const string& sData) { + string sLine = sData; + + while ((CUtils::Right(sLine, 1) == "\r") || (CUtils::Right(sLine, 1) == "\n")) { + CUtils::RightChomp(sLine); + } + + DEBUG_ONLY(cout << GetSockName() << " <- [" << sLine << "]" << endl); + + PutPeer(sLine); +} + +void CDCCBounce::ReadData(const char* data, int len) { + if (m_pPeer) { + m_pPeer->Write(data, len); + } +} + +void CDCCBounce::Timeout() { + DEBUG_ONLY(cout << GetSockName() << " == Timeout()" << endl); + string sType = (m_bIsChat) ? "Chat" : "Xfer"; + + if (IsRemote()) { + string sHost = Csock::GetHostName(); + if (!sHost.empty()) { + sHost = " to [" + sHost + ":" + CUtils::ToString(Csock::GetPort()) + "]"; + } else { + sHost = "."; + } + + m_pUser->PutStatus("DCC " + sType + " Bounce (" + m_sRemoteNick + "): Timeout while connecting" + sHost); + } else { + m_pUser->PutStatus("DCC " + sType + " Bounce (" + m_sRemoteNick + "): Timeout waiting for incoming connection [" + Csock::GetLocalIP() + ":" + CUtils::ToString(Csock::GetLocalPort()) + "]"); + } +} + +void CDCCBounce::ConnectionRefused() { + DEBUG_ONLY(cout << GetSockName() << " == ConnectionRefused()" << endl); + + string sType = (m_bIsChat) ? "Chat" : "Xfer"; + string sHost = Csock::GetHostName(); + if (!sHost.empty()) { + sHost = " to [" + sHost + ":" + CUtils::ToString(Csock::GetPort()) + "]"; + } else { + sHost = "."; + } + + m_pUser->PutStatus("DCC " + sType + " Bounce (" + m_sRemoteNick + "): Connection Refused while connecting" + sHost); +} + +void CDCCBounce::SockError(int iErrno) { + DEBUG_ONLY(cout << GetSockName() << " == SockError(" << iErrno << ")" << endl); + string sType = (m_bIsChat) ? "Chat" : "Xfer"; + + if (IsRemote()) { + string sHost = Csock::GetHostName(); + if (!sHost.empty()) { + sHost = "[" + sHost + ":" + CUtils::ToString(Csock::GetPort()) + "]"; + } + + m_pUser->PutStatus("DCC " + sType + " Bounce (" + m_sRemoteNick + "): Socket error [" + string(strerror(iErrno)) + "]" + sHost); + } else { + m_pUser->PutStatus("DCC " + sType + " Bounce (" + m_sRemoteNick + "): Socket error [" + string(strerror(iErrno)) + "] [" + Csock::GetLocalIP() + ":" + CUtils::ToString(Csock::GetLocalPort()) + "]"); + } +} + +void CDCCBounce::Connected() { + DEBUG_ONLY(cout << GetSockName() << " == Connected()" << endl); + SetTimeout(0); +} + +void CDCCBounce::Disconnected() { + DEBUG_ONLY(cout << GetSockName() << " == Disconnected()" << endl); +} + +void CDCCBounce::Shutdown() { + m_pPeer = NULL; + DEBUG_ONLY(cerr << GetSockName() << " == Close(); because my peer told me to" << endl); + Close(); +} + +Csock* CDCCBounce::GetSockObj(const string& sHost, int iPort) { + Close(); + + if (!m_pManager) { + return NULL; + } + + if (m_sRemoteIP.empty()) { + m_sRemoteIP = sHost; + } + + CDCCBounce* pSock = new CDCCBounce(sHost, iPort, m_pUser, m_sRemoteNick, m_sRemoteIP, m_sFileName, m_bIsChat); + CDCCBounce* pRemoteSock = new CDCCBounce(sHost, iPort, m_pUser, m_sRemoteNick, m_sRemoteIP, m_sFileName, m_bIsChat); + pSock->SetPeer(pRemoteSock); + pRemoteSock->SetPeer(pSock); + pRemoteSock->SetRemote(true); + pSock->SetRemote(false); + + if (!m_pManager->Connect(m_sConnectIP, m_uRemotePort, "DCC::" + string((m_bIsChat) ? "Chat" : "XFER") + "::Remote::" + m_sRemoteNick, 60, false, m_sLocalIP, pRemoteSock)) { + pRemoteSock->Close(); + } + + pSock->SetSockName(GetSockName()); + pSock->SetTimeout(0); + return pSock; +} + +void CDCCBounce::PutServ(const string& sLine) { + DEBUG_ONLY(cout << GetSockName() << " -> [" << sLine << "]" << endl); + Write(sLine + "\r\n"); +} + +void CDCCBounce::PutPeer(const string& sLine) { + if (m_pPeer) { + m_pPeer->PutServ(sLine); + } else { + PutServ("*** Not connected yet ***"); + } +} + +unsigned short CDCCBounce::DCCRequest(const string& sNick, unsigned long uLongIP, unsigned short uPort, const string& sFileName, bool bIsChat, CUser* pUser, const string& sLocalIP, const string& sRemoteIP) { + CDCCBounce* pDCCBounce = new CDCCBounce(pUser, uLongIP, uPort, sFileName, sNick, sRemoteIP, sLocalIP, bIsChat); + unsigned short uListenPort = pUser->GetManager()->ListenAllRand("DCC::" + string((bIsChat) ? "Chat" : "Xfer") + "::Local::" + sNick, false, SOMAXCONN, pDCCBounce, 120); + + return uListenPort; +} + diff --git a/DCCBounce.h b/DCCBounce.h new file mode 100644 index 00000000..01b59d71 --- /dev/null +++ b/DCCBounce.h @@ -0,0 +1,98 @@ +#ifndef _DCCBOUNCE_H +#define _DCCBOUNCE_H + +#include "main.h" +#include "Utils.h" +#include "User.h" + +class CDCCBounce : public Csock { +public: + CDCCBounce(CUser* pUser, unsigned long uLongIP, unsigned short uPort, const string& sFileName, const string& sRemoteNick, const string& sRemoteIP, string sLocalIP, bool bIsChat = false) : Csock() { + m_uRemotePort = uPort; + m_sConnectIP = CUtils::GetIP(uLongIP); + m_sRemoteIP = sRemoteIP; + m_sFileName = sFileName; + m_sRemoteNick = sRemoteNick; + m_pUser = pUser; + m_pManager = pUser->GetManager(); + m_bIsChat = bIsChat; + m_sLocalIP = sLocalIP; + m_pPeer = NULL; + m_bIsRemote = false; + + if (bIsChat) { + EnableReadLine(); + } + } + + CDCCBounce(const string& sHostname, int iport, CUser* pUser, const string& sRemoteNick, const string& sRemoteIP, const string& sFileName, int itimeout = 60, bool bIsChat = false) : Csock(sHostname, iport, itimeout) { + m_uRemotePort = 0; + m_bIsChat = bIsChat; + m_pManager = pUser->GetManager(); + m_pUser = pUser; + m_pPeer = NULL; + m_sRemoteNick = sRemoteNick; + m_sFileName = sFileName; + m_sRemoteIP = sRemoteIP; + m_bIsRemote = false; + + if (bIsChat) { + EnableReadLine(); + } + } + virtual ~CDCCBounce() { + if (m_pPeer) { + m_pPeer->Shutdown(); + m_pPeer = NULL; + } + } + + static unsigned short DCCRequest(const string& sNick, unsigned long uLongIP, unsigned short uPort, const string& sFileName, bool bIsChat, CUser* pUser, const string& sLocalIP, const string& sRemoteIP); + + void ReadLine(const string& sData); + virtual void ReadData(const char* data, int len); + virtual void Timeout(); + virtual void ConnectionRefused(); + virtual void SockError(int iErrno); + virtual void Connected(); + virtual void Disconnected(); + void Shutdown(); + Csock* GetSockObj(const string& sHost, int iPort); + void PutServ(const string& sLine); + void PutPeer(const string& sLine); + bool IsPeerConnected() { return (m_pPeer) ? m_pPeer->IsConnected() : false; } + + // Setters + void SetPeer(CDCCBounce* p) { m_pPeer = p; } + void SetRemoteIP(const string& s) { m_sRemoteIP = s; } + void SetRemoteNick(const string& s) { m_sRemoteNick = s; } + void SetManager(TSocketManager* p) { m_pManager = p; } + void SetRemote(bool b) { m_bIsRemote = b; } + // !Setters + + // Getters + unsigned short GetUserPort() const { return m_uRemotePort; } + const string& GetRemoteIP() const { return m_sRemoteIP; } + const string& GetRemoteNick() const { return m_sRemoteNick; } + const string& GetFileName() const { return m_sFileName; } + CDCCBounce* GetPeer() const { return m_pPeer; } + TSocketManager* GetManager() const { return m_pManager; } + bool IsRemote() { return m_bIsRemote; } + // !Getters +private: +protected: + string m_sRemoteNick; + string m_sRemoteIP; + string m_sConnectIP; + string m_sLocalIP; + string m_sFileName; + CUser* m_pUser; + CDCCBounce* m_pPeer; + TSocketManager* m_pManager; + unsigned short m_uRemotePort; + bool m_bIsChat; + bool m_bIsRemote; +}; + +#endif // !_DCCBOUNCE_H + diff --git a/DCCSock.cpp b/DCCSock.cpp new file mode 100644 index 00000000..23bc7fe8 --- /dev/null +++ b/DCCSock.cpp @@ -0,0 +1,156 @@ +#include "DCCSock.h" + +void CDCCSock::ReadData(const char* data, int len) { + if (!m_pFile) { + DEBUG_ONLY(cout << "File not open! closing get." << endl); + m_pUser->PutModule(m_sModuleName, ((m_bSend) ? "DCC -> [" : "DCC <- [") + m_sRemoteNick + "][" + m_sFileName + "] - File not open!"); + Close(); + } + + if (m_bSend) { + m_sSendBuf.append(data, len); + + while (m_sSendBuf.size() >= 4) { + unsigned int iRemoteSoFar; + memcpy(&iRemoteSoFar, m_sSendBuf.data(), 4); + iRemoteSoFar = ntohl(iRemoteSoFar); + + if (iRemoteSoFar >= m_uBytesSoFar) { + SendPacket(); + } + + m_sSendBuf.erase(0, 4); + } + } else { + m_pFile->Write(data, len); + m_uBytesSoFar += len; + unsigned long uSoFar = htonl(m_uBytesSoFar); + Write((char*) &uSoFar, sizeof(unsigned long)); + + if (m_uBytesSoFar >= m_uFileSize) { + Close(); + } + } +} + +void CDCCSock::ConnectionRefused() { + DEBUG_ONLY(cout << GetSockName() << " == ConnectionRefused()" << endl); + m_pUser->PutModule(m_sModuleName, ((m_bSend) ? "DCC -> [" : "DCC <- [") + m_sRemoteNick + "][" + m_sFileName + "] - Connection Refused."); +} + +void CDCCSock::Timeout() { + DEBUG_ONLY(cout << GetSockName() << " == Timeout()" << endl); + m_pUser->PutModule(m_sModuleName, ((m_bSend) ? "DCC -> [" : "DCC <- [") + m_sRemoteNick + "][" + m_sFileName + "] - Timed Out."); +} + +void CDCCSock::SockError(int iErrno) { + DEBUG_ONLY(cout << GetSockName() << " == SockError(" << iErrno << ")" << endl); + m_pUser->PutModule(m_sModuleName, ((m_bSend) ? "DCC -> [" : "DCC <- [") + m_sRemoteNick + "][" + m_sFileName + "] - Socket Error [" + CUtils::ToString(iErrno) + "]"); +} + +void CDCCSock::Connected() { + DEBUG_ONLY(cout << GetSockName() << " == Connected(" << GetRemoteIP() << ")" << endl); + m_pUser->PutModule(m_sModuleName, ((m_bSend) ? "DCC -> [" : "DCC <- [") + m_sRemoteNick + "][" + m_sFileName + "] - Transfer Started."); + + if (m_bSend) { + SendPacket(); + } + + SetTimeout(120); +} + +void CDCCSock::Disconnected() { + DEBUG_ONLY(cout << GetSockName() << " == Disconnected()" << endl); + if (m_uBytesSoFar > m_uFileSize) { + m_pUser->PutModule(m_sModuleName, ((m_bSend) ? "DCC -> [" : "DCC <- [") + m_sRemoteNick + "][" + m_sFileName + "] - TooMuchData!"); + } else if (m_uBytesSoFar == m_uFileSize) { + if (m_bSend) { + m_pUser->PutModule(m_sModuleName, ((m_bSend) ? "DCC -> [" : "DCC <- [") + m_sRemoteNick + "][" + m_sFileName + "] - Completed! - Sent [" + m_sLocalFile + "] at [" + CUtils::ToKBytes(GetAvgWrite() / 1000.0) + "]"); + } else { + m_pUser->PutModule(m_sModuleName, ((m_bSend) ? "DCC -> [" : "DCC <- [") + m_sRemoteNick + "][" + m_sFileName + "] - Completed! - Saved to [" + m_sLocalFile + "] at [" + CUtils::ToKBytes(GetAvgRead() / 1000.0) + "]"); + } + } else { + m_pUser->PutModule(m_sModuleName, ((m_bSend) ? "DCC -> [" : "DCC <- [") + m_sRemoteNick + "][" + m_sFileName + "] - Incomplete!"); + } +} + +void CDCCSock::SendPacket() { + if (!m_pFile) { + m_pUser->PutModule(m_sModuleName, ((m_bSend) ? "DCC -> [" : "DCC <- [") + m_sRemoteNick + "][" + m_sFileName + "] - File closed prematurely."); + Close(); + return; + } + + char szBuf[4096]; + int iLen = m_pFile->Read(szBuf, 4096); + + if (iLen < 0) { + m_pUser->PutModule(m_sModuleName, ((m_bSend) ? "DCC -> [" : "DCC <- [") + m_sRemoteNick + "][" + m_sFileName + "] - Error reading from file."); + Close(); + return; + } + + if (iLen > 0) { + Write(szBuf, iLen); + m_uBytesSoFar += iLen; + } +} + +Csock* CDCCSock::GetSockObj(const string& sHost, int iPort) { + Close(); + + CDCCSock* pSock = new CDCCSock(m_pUser, m_sRemoteNick, m_sLocalFile, m_sModuleName, m_uFileSize, m_pFile); + pSock->SetSockName("DCC::SEND::" + m_sRemoteNick); + pSock->SetTimeout(120); + pSock->SetFileName(m_sFileName); + pSock->SetFileOffset(m_uBytesSoFar); + m_bNoDelFile = true; + + return pSock; +} + +CFile* CDCCSock::OpenFile(bool bWrite) { + if ((m_pFile) || (m_sLocalFile.empty())) { + m_pUser->PutModule(m_sModuleName, ((bWrite) ? "DCC <- [" : "DCC -> [") + m_sRemoteNick + "][" + m_sLocalFile + "] - Unable to open file."); + return false; + } + + m_pFile = new CFile(m_sLocalFile); + + if (bWrite) { + if (m_pFile->Exists()) { + delete m_pFile; + m_pFile = NULL; + m_pUser->PutModule(m_sModuleName, "DCC <- [" + m_sRemoteNick + "] - File already exists [" + m_sLocalFile + "]"); + return NULL; + } + + if (!m_pFile->Open(O_WRONLY | O_TRUNC | O_CREAT)) { + delete m_pFile; + m_pFile = NULL; + m_pUser->PutModule(m_sModuleName, "DCC <- [" + m_sRemoteNick + "] - Could not open file [" + m_sLocalFile + "]"); + return NULL; + } + } else { + if (!m_pFile->IsReg()) { + delete m_pFile; + m_pFile = NULL; + m_pUser->PutModule(m_sModuleName, "DCC -> [" + m_sRemoteNick + "] - Not a file [" + m_sLocalFile + "]"); + return NULL; + } + + if (!m_pFile->Open(O_RDONLY)) { + delete m_pFile; + m_pFile = NULL; + m_pUser->PutModule(m_sModuleName, "DCC -> [" + m_sRemoteNick + "] - Could not open file [" + m_sLocalFile + "]"); + return NULL; + } + + m_uFileSize = m_pFile->GetSize(); + } + + m_sFileName = m_pFile->GetShortName(); + + return m_pFile; +} + diff --git a/DCCSock.h b/DCCSock.h new file mode 100644 index 00000000..e06cd994 --- /dev/null +++ b/DCCSock.h @@ -0,0 +1,108 @@ +#ifndef _DCCSOCK_H +#define _DCCSOCK_H + +#include "main.h" +#include "Utils.h" +#include "User.h" + +class CDCCSock : public Csock { +public: + CDCCSock(CUser* pUser, const string& sRemoteNick, const string& sLocalFile, const string& sModuleName, unsigned long uFileSize = 0, CFile* pFile = NULL) : Csock() { + m_sRemoteNick = sRemoteNick; + m_uFileSize = uFileSize; + m_uRemotePort = 0; + m_uBytesSoFar = 0; + m_pUser = pUser; + m_pFile = pFile; + m_sLocalFile = sLocalFile; + m_sModuleName = sModuleName; + m_bSend = true; + m_bNoDelFile = false; + } + + CDCCSock(CUser* pUser, const string& sRemoteNick, const string& sRemoteIP, unsigned short uRemotePort, const string& sLocalFile, unsigned long uFileSize, const string& sModuleName) : Csock() { + m_sRemoteNick = sRemoteNick; + m_sRemoteIP = sRemoteIP; + m_uRemotePort = uRemotePort; + m_uFileSize = uFileSize; + m_uBytesSoFar = 0; + m_pUser = pUser; + m_pFile = NULL; + m_sLocalFile = sLocalFile; + m_sModuleName = sModuleName; + m_bSend = false; + m_bNoDelFile = false; + } + +/* CDCCSock(CUser* pUser, const string& sHostname, int iport, int itimeout = 60) : Csock(sHostname, iport, itimeout) { + m_uRemotePort = 0; + m_uBytesSoFar = 0; + m_uFileSize = 0; + m_pFile = NULL; + m_pUser = pUser; + m_bNoDelFile = false; + } +*/ + virtual ~CDCCSock() { + if ((m_pFile) && (!m_bNoDelFile)) { + m_pFile->Close(); + delete m_pFile; + } + } + + virtual void ReadData(const char* data, int len); + virtual void ConnectionRefused(); + virtual void SockError(int iErrno); + virtual void Timeout(); + virtual void Connected(); + virtual void Disconnected(); + void SendPacket(); + Csock* GetSockObj(const string& sHost, int iPort); + CFile* OpenFile(bool bWrite = true); + bool Seek(unsigned int uPos) { + if (m_pFile) { + if (m_pFile->Seek(uPos)) { + m_uBytesSoFar = uPos; + return true; + } + } + + return false; + } + + // Setters + void SetRemoteIP(const string& s) { m_sRemoteIP = s; } + void SetRemoteNick(const string& s) { m_sRemoteNick = s; } + void SetFileName(const string& s) { m_sFileName = s; } + void SetFileOffset(unsigned long u) { m_uBytesSoFar = u; } + // !Setters + + // Getters + unsigned short GetUserPort() const { return m_uRemotePort; } + const string& GetRemoteNick() const { return m_sRemoteNick; } + const string& GetFileName() const { return m_sFileName; } + const string& GetLocalFile() const { return m_sLocalFile; } + const string& GetModuleName() const { return m_sModuleName; } + CFile* GetFile() { return m_pFile; } + double GetProgress() const { return ((m_uFileSize) && (m_uBytesSoFar)) ? (double) (((double) m_uBytesSoFar / (double) m_uFileSize) *100.0) : 0; } + //const string& GetRemoteIP() const { return m_sRemoteIP; } + // !Getters +private: +protected: + string m_sRemoteNick; + string m_sRemoteIP; + string m_sFileName; + string m_sLocalFile; + string m_sSendBuf; + string m_sModuleName; + unsigned short m_uRemotePort; + unsigned long m_uFileSize; + unsigned long m_uBytesSoFar; + bool m_bSend; + bool m_bNoDelFile; + CFile* m_pFile; + CUser* m_pUser; +}; + +#endif // !_DCCSOCK_H + diff --git a/IRCSock.cpp b/IRCSock.cpp new file mode 100644 index 00000000..4b186253 --- /dev/null +++ b/IRCSock.cpp @@ -0,0 +1,742 @@ +#include "znc.h" +#include "IRCSock.h" +#include "DCCBounce.h" +#include "UserSock.h" + +CIRCSock::CIRCSock(CZNC* pZNC, CUser* pUser) : Csock() { + m_pZNC = pZNC; + m_pUser = pUser; + m_pUserSock = NULL; + m_uQueryBufferCount = 50; + m_bISpoofReleased = false; + m_bKeepNick = true; + m_bAuthed = false; + EnableReadLine(); + m_RawBuffer.SetLineCount(100); // This should be more than enough raws, especially since we are asking the server to resend MOTD + m_Nick.SetIdent(pUser->GetIdent()); + m_Nick.SetHost(pUser->GetVHost()); +} + +CIRCSock::~CIRCSock() { + for (map::iterator a = m_msChans.begin(); a != m_msChans.end(); a++) { + delete a->second; + } + + if (!m_bISpoofReleased) { + m_pZNC->ReleaseISpoof(); + } + + PutServ("QUIT :"); + m_msChans.clear(); +} + +void CIRCSock::ReadLine(const string& sData) { + string sLine = sData; + + while ((CUtils::Right(sLine, 1) == "\r") || (CUtils::Right(sLine, 1) == "\n")) { + CUtils::RightChomp(sLine); + } + + DEBUG_ONLY(cout << GetSockName() << " <- [" << sLine << "]" << endl); + +#ifdef _MODULES + if (m_pUser->GetModules().OnRaw(sLine)) { + return; + } +#endif + + if (strncasecmp(sLine.c_str(), "PING ", 5) == 0) { + PutServ("PONG " + sLine.substr(5)); + } else if (CUtils::WildCmp(":* * *", sLine.c_str())) { //"^:(\\S+) (\\d\\d\\d) (.*?) (.*)$", vCap)) { + string sCmd = CUtils::Token(sLine, 1); + + if ((sCmd.length() == 3) && (isdigit(sCmd[0])) && (isdigit(sCmd[1])) && (isdigit(sCmd[2]))) { + string sServer = CUtils::Token(sLine, 0); CUtils::LeftChomp(sServer); + unsigned int uRaw = strtoul(sCmd.c_str(), NULL, 10); + string sNick = CUtils::Token(sLine, 2); + string sRest = CUtils::Token(sLine, 3, true); + + switch (uRaw) { + case 1: {// :irc.server.com 001 nick :Welcome to the Internet Relay Network nick + SetTimeout(900); // Now that we are connected, let nature take its course + PutServ("WHO " + sNick); +#ifdef _MODULES + m_pUser->GetModules().OnIRCConnected(); +#endif + m_bAuthed = true; + m_pUser->PutStatus("Connected!"); + + if (m_pUserSock) { + string sClientNick = m_pUserSock->GetNick(); + if (strcasecmp(sClientNick.c_str(), sNick.c_str()) != 0) { + // If they connected with a nick that doesn't match the one we got on irc, then we need to update them + PutUser(":" + sClientNick + "!" + m_Nick.GetIdent() + "@" + m_Nick.GetHost() + " NICK :" + sNick); + } + } + + SetNick(sNick); + + m_RawBuffer.Clear(); + m_RawBuffer.AddLine(":" + sServer + " " + sCmd + " ", " " + sRest); + + // Now that we are connected, we need to join our chans + const vector& vChans = m_pUser->GetChans(); + + for (unsigned int a = 0; a < vChans.size(); a++) { + PutServ("JOIN " + vChans[a]->GetName() + " " + vChans[a]->GetKey()); + } + + m_pZNC->ReleaseISpoof(); + m_bISpoofReleased = true; + + break; + } + case 2: + case 3: + case 4: + case 5: + case 250: // highest connection count + case 251: // user count + case 252: // oper count + case 254: // channel count + case 255: // client count + case 265: // local users + case 266: // global users + m_RawBuffer.AddLine(":" + sServer + " " + sCmd + " ", " " + sRest); + break; + case 372: // motd + case 375: // begin motd + case 376: // end motd + break; + case 471: // :irc.server.net 471 nick #chan :Cannot join channel (+l) + case 473: // :irc.server.net 473 nick #chan :Cannot join channel (+i) + case 475: // :irc.server.net 475 nick #chan :Cannot join channel (+k) + if (m_pUserSock) { + CChan* pChan = m_pUser->FindChan(sRest.substr(0, sRest.find(' '))); + + if ((pChan) && (!pChan->IsOn())) { + if (!pChan->DecClientRequests()) { + return; + } + } + } + + break; + case 433: { + string sBadNick = CUtils::Token(sRest, 0); + string sConfNick = m_pUser->GetNick(); + + if (sNick == "*") { + string sAltNick = m_pUser->GetAltNick(); + + if (strcasecmp(sBadNick.c_str(), sConfNick.c_str()) == 0) { + if ((!sAltNick.empty()) && (strcasecmp(sConfNick.c_str(), sAltNick.c_str()) != 0)) { + PutServ("NICK " + sAltNick); + } else { + PutServ("NICK " + CUtils::Left(sConfNick, 8) + "-"); + } + } else if (strcasecmp(sBadNick.c_str(), sAltNick.c_str()) == 0) { + PutServ("NICK " + CUtils::Left(sConfNick, 8) + "-"); + } else if (strcasecmp(sBadNick.c_str(), string(CUtils::Left(sConfNick, 8) + "-").c_str()) == 0) { + PutServ("NICK " + CUtils::Left(sConfNick, 8) + "|"); + } else if (strcasecmp(sBadNick.c_str(), string(CUtils::Left(sConfNick, 8) + "|").c_str()) == 0) { + PutServ("NICK " + CUtils::Left(sConfNick, 8) + "^"); + } else { + char cLetter = 0; + if (sBadNick.empty()) { + Close(); + return; + } + + cLetter = CUtils::Right(sBadNick, 1)[0]; + + if (cLetter == 'z') { + Close(); + return; + } + + string sSend = "NICK " + CUtils::Left(sConfNick, 8) + cLetter++; + PutServ(sSend); + } + + return; + } else { + // :irc.server.net 433 mynick badnick :Nickname is already in use. + if ((m_bKeepNick) && (m_pUser->KeepNick())) { + if (strcasecmp(sBadNick.c_str(), sConfNick.c_str()) == 0) { + if ((!m_pUserSock) || (!m_pUserSock->DecKeepNickCounter())) { + return; + } + } + } + } + break; + } + case 315: { + // :irc.server.com 315 yournick #chan :End of /WHO list. + CChan* pChan = m_pUser->FindChan(CUtils::Token(sLine, 3)); + + if (pChan) { + pChan->SetWhoDone(); + } + } + case 352: { + // :irc.yourserver.com 352 yournick #chan ident theirhost.com irc.theirserver.com theirnick H :0 Real Name + string sNick = CUtils::Token(sLine, 7); + string sIdent = CUtils::Token(sLine, 4); + string sHost = CUtils::Token(sLine, 5); + + if (strcasecmp(sNick.c_str(), GetNick().c_str()) == 0) { + m_Nick.SetIdent(sIdent); + m_Nick.SetHost(sHost); + } + + const vector& vChans = m_pUser->GetChans(); + + for (unsigned int a = 0; a < vChans.size(); a++) { + vChans[a]->OnWho(sNick, sIdent, sHost); + } + + break; + } + case 324: { // MODE + CUtils::Trim(sRest); + CChan* pChan = m_pUser->FindChan(CUtils::Token(sRest, 0)); + + if (pChan) { + pChan->SetModes(CUtils::Token(sRest, 1, true)); + } + } + break; + case 353: { // NAMES + CUtils::Trim(sRest); + // Todo: allow for non @+= server msgs + CChan* pChan = m_pUser->FindChan(CUtils::Token(sRest, 1)); + if (pChan) { + string sNicks = CUtils::Token(sRest, 2, true); + if (CUtils::Left(sNicks, 1) == ":") { + CUtils::LeftChomp(sNicks); + } + + pChan->AddNicks(sNicks); + } + } + break; + case 366: { // end of names list + PutUser(sLine); // First send them the raw + + // :irc.server.com 366 nick #chan :End of /NAMES list. + CChan* pChan = m_pUser->FindChan(CUtils::Token(sRest, 0)); + + if (pChan) { + if (IsUserAttached()) { + const vector& vsBuffer = pChan->GetBuffer(); + + if (vsBuffer.size()) { + PutUser(":***!znc@znc.com PRIVMSG " + pChan->GetName() + " :Buffer Playback..."); + + for (unsigned int a = 0; a < vsBuffer.size(); a++) { + PutUser(vsBuffer[a]); + } + + if (!pChan->KeepBuffer()) { + pChan->ClearBuffer(); + } + + PutUser(":***!znc@znc.com PRIVMSG " + pChan->GetName() + " :Playback Complete."); + } + } + + if (!pChan->IsOn()) { + pChan->SetIsOn(true); + PutServ("MODE " + pChan->GetName()); + + // If we are the only one in the chan, set our default modes + if (pChan->GetNickCount() == 1) { + string sModes = pChan->GetDefaultModes(); + + if (sModes.empty()) { + sModes = m_pUser->GetDefaultChanModes(); + } + + if (!sModes.empty()) { + PutServ("MODE " + pChan->GetName() + " " + sModes); + } + } + } + } + + return; // return so we don't send them the raw twice + } + } + } else { //if (CUtils::WildCmp(":*!*@* * *", sLine.c_str())) { + string sNickMask = CUtils::Token(sLine, 0); + CUtils::LeftChomp(sNickMask); + + string sNick = CUtils::Token(sNickMask, 0, false, '!'); + string sCmd = CUtils::Token(sLine, 1); + string sRest = CUtils::Token(sLine, 2, true); + + if (strcasecmp(sCmd.c_str(), "NICK") == 0) { + string sNewNick = sRest; + if (CUtils::Left(sNewNick, 1) == ":") { + CUtils::LeftChomp(sNewNick); + } + + const vector& vChans = m_pUser->GetChans(); + for (unsigned int a = 0; a < vChans.size(); a++) { + vChans[a]->ChangeNick(sNick, sNewNick); + } + + if (strcasecmp(sNick.c_str(), GetNick().c_str()) == 0) { + SetNick(sNewNick); + if (strcasecmp(sNick.c_str(), m_pUser->GetNick().c_str()) == 0) { + // If the user changes his nick away from the config nick, we shut off keepnick for this session + m_bKeepNick = false; + } + } else if (strcasecmp(sNick.c_str(), m_pUser->GetNick().c_str()) == 0) { + KeepNick(); + } +#ifdef _MODULES + m_pUser->GetModules().OnNick(sNickMask, sNewNick); +#endif + } else if (strcasecmp(sCmd.c_str(), "QUIT") == 0) { + string sChan = sRest; + if (CUtils::Left(sChan, 1) == ":") { + CUtils::LeftChomp(sChan); + } + + // :nick!ident@host.com QUIT :message + CNick Nick(sNickMask); + + const vector& vChans = m_pUser->GetChans(); + for (unsigned int a = 0; a < vChans.size(); a++) { + vChans[a]->RemNick(sNick); + } + + if (strcasecmp(Nick.GetNick().c_str(), m_pUser->GetNick().c_str()) == 0) { + KeepNick(); + } + +#ifdef _MODULES + m_pUser->GetModules().OnQuit(Nick, sChan); +#endif + } else if (strcasecmp(sCmd.c_str(), "JOIN") == 0) { + string sChan = sRest; + if (CUtils::Left(sChan, 1) == ":") { + CUtils::LeftChomp(sChan); + } + + if (strcasecmp(sNick.c_str(), GetNick().c_str()) == 0) { + m_pUser->AddChan(sChan); + } + + CChan* pChan = m_pUser->FindChan(sChan); + if (pChan) { + pChan->AddNick(sNickMask); +#ifdef _MODULES + m_pUser->GetModules().OnJoin(sNickMask, *pChan); +#endif + } + } else if (strcasecmp(sCmd.c_str(), "PART") == 0) { + string sChan = sRest; + if (CUtils::Left(sChan, 1) == ":") { + CUtils::LeftChomp(sChan); + } + + CChan* pChan = m_pUser->FindChan(sChan); + if (pChan) { + pChan->RemNick(sNick); +#ifdef _MODULES + m_pUser->GetModules().OnPart(sNickMask, *pChan); +#endif + } + + if (strcasecmp(sNick.c_str(), GetNick().c_str()) == 0) { + m_pUser->DelChan(sChan); + } + } else if (strcasecmp(sCmd.c_str(), "MODE") == 0) { + string sChan = CUtils::Token(sRest, 0); + string sModes = CUtils::Token(sRest, 1, true); + + CChan* pChan = m_pUser->FindChan(sChan); + if (pChan) { + pChan->ModeChange(sModes, sNick); + } + } else if (strcasecmp(sCmd.c_str(), "KICK") == 0) { + // :opnick!ident@host.com KICK #chan nick :msg + string sChan = CUtils::Token(sRest, 0); + string sKickedNick = CUtils::Token(sRest, 1); + string sMsg = CUtils::Token(sRest, 2, true); + CUtils::LeftChomp(sMsg); + + CChan* pChan = m_pUser->FindChan(sChan); + + if (pChan) { + pChan->RemNick(sKickedNick); +#ifdef _MODULES + m_pUser->GetModules().OnKick(sNickMask, sKickedNick, *pChan, sMsg); +#endif + } + + if (strcasecmp(GetNick().c_str(), sKickedNick.c_str()) == 0) { + string sKey; + + if (pChan) { + sKey = pChan->GetKey(); + pChan->SetIsOn(false); + } + + PutServ("JOIN " + sChan + " " + sKey); + } + } else if (strcasecmp(sCmd.c_str(), "NOTICE") == 0) { + // :nick!ident@host.com NOTICE #chan :Message + + CNick Nick(sNickMask); + + string sTarget = CUtils::Token(sRest, 0); + string sMsg = CUtils::Token(sRest, 1, true); + CUtils::LeftChomp(sMsg); + + if (CUtils::WildCmp("\001*\001", sMsg.c_str())) { + CUtils::LeftChomp(sMsg); + CUtils::RightChomp(sMsg); + + if (strcasecmp(sTarget.c_str(), GetNick().c_str()) == 0) { + if (OnCTCPReply(sNickMask, sMsg)) { + return; + } + } + + PutUser(":" + sNickMask + " NOTICE " + sTarget + " :\001" + sMsg + "\001"); + return; + } else { + if (strcasecmp(sTarget.c_str(), GetNick().c_str()) == 0) { + if (OnPrivNotice(sNickMask, sMsg)) { + return; + } + } else { + if (OnChanNotice(sNickMask, sTarget, sMsg)) { + return; + } + } + } + + PutUser(":" + sNickMask + " NOTICE " + sTarget + " :" + sMsg); + return; + } else if (strcasecmp(sCmd.c_str(), "PRIVMSG") == 0) { + // :nick!ident@host.com PRIVMSG #chan :Message + + CNick Nick(sNickMask); + string sTarget = CUtils::Token(sRest, 0); + string sMsg = CUtils::Token(sRest, 1, true); + + if (CUtils::Left(sMsg, 1) == ":") { + CUtils::LeftChomp(sMsg); + } + + if (CUtils::WildCmp("\001*\001", sMsg.c_str())) { + CUtils::LeftChomp(sMsg); + CUtils::RightChomp(sMsg); + + if (strcasecmp(sTarget.c_str(), GetNick().c_str()) == 0) { + if (OnPrivCTCP(sNickMask, sMsg)) { + return; + } + } else { + if (OnChanCTCP(sNickMask, sTarget, sMsg)) { + return; + } + } + + PutUser(":" + sNickMask + " PRIVMSG " + sTarget + " :\001" + sMsg + "\001"); + return; + } else { + if (strcasecmp(sTarget.c_str(), GetNick().c_str()) == 0) { + if (OnPrivMsg(sNickMask, sMsg)) { + return; + } + } else { + if (OnChanMsg(sNickMask, sTarget, sMsg)) { + return; + } + } + + PutUser(":" + sNickMask + " PRIVMSG " + sTarget + " :" + sMsg); + return; + } + } + } + } + + PutUser(sLine); +} + +void CIRCSock::KeepNick() { + const string& sConfNick = m_pUser->GetNick(); + + if ((m_bAuthed) && (m_bKeepNick) && (m_pUser->KeepNick()) && (strcasecmp(GetNick().c_str(), sConfNick.c_str()) != 0)) { + PutServ("NICK " + sConfNick); + } +} + +bool CIRCSock::OnCTCPReply(const string& sNickMask, string& sMessage) { +#ifdef _MODULES + if (m_pUser->GetModules().OnCTCPReply(sNickMask, sMessage)) { + return true; + } +#endif + + return false; +} + +bool CIRCSock::OnPrivCTCP(const string& sNickMask, string& sMessage) { +#ifdef _MODULES + if (m_pUser->GetModules().OnPrivCTCP(sNickMask, sMessage)) { + return true; + } +#endif + + // DCC CHAT chat 2453612361 44592 + if (strncasecmp(sMessage.c_str(), "DCC ", 4) == 0) { + string sType = CUtils::Token(sMessage, 1); + string sFile = CUtils::Token(sMessage, 2); + unsigned long uLongIP = strtoul(CUtils::Token(sMessage, 3).c_str(), NULL, 10); + unsigned short uPort = strtoul(CUtils::Token(sMessage, 4).c_str(), NULL, 10); + unsigned long uFileSize = strtoul(CUtils::Token(sMessage, 5).c_str(), NULL, 10); + + if (strcasecmp(sType.c_str(), "CHAT") == 0) { + if (m_pUserSock) { + CNick FromNick(sNickMask); + unsigned short uBNCPort = CDCCBounce::DCCRequest(FromNick.GetNick(), uLongIP, uPort, "", true, m_pUser, GetLocalIP(), CUtils::GetIP(uLongIP)); + + if (uBNCPort) { + PutUser(":" + sNickMask + " PRIVMSG " + GetNick() + " :\001DCC CHAT chat " + CUtils::ToString(CUtils::GetLongIP(GetLocalIP())) + " " + CUtils::ToString(uBNCPort) + "\001"); + } + } + } else if (strcasecmp(sType.c_str(), "SEND") == 0) { + // DCC SEND readme.txt 403120438 5550 1104 + CNick FromNick(sNickMask); + + unsigned short uBNCPort = CDCCBounce::DCCRequest(FromNick.GetNick(), uLongIP, uPort, sFile, false, m_pUser, GetLocalIP(), CUtils::GetIP(uLongIP)); + if (uBNCPort) { + PutUser(":" + sNickMask + " PRIVMSG " + GetNick() + " :\001DCC SEND " + sFile + " " + CUtils::ToString(CUtils::GetLongIP(GetLocalIP())) + " " + CUtils::ToString(uBNCPort) + " " + CUtils::ToString(uFileSize)); + } + } else if (strcasecmp(sType.c_str(), "RESUME") == 0) { + // Need to lookup the connection by port, filter the port, and forward to the user + CDCCBounce* pSock = (CDCCBounce*) m_pZNC->GetManager().FindSockByLocalPort(atoi(CUtils::Token(sMessage, 3).c_str())); + + if ((pSock) && (strncasecmp(pSock->GetSockName().c_str(), "DCC::", 5) == 0)) { + PutUser(":" + sNickMask + " PRIVMSG " + GetNick() + " :\001DCC " + sType + " " + sFile + " " + CUtils::ToString(pSock->GetUserPort()) + " " + CUtils::Token(sMessage, 4) + "\001"); + } + } else if (strcasecmp(sType.c_str(), "ACCEPT") == 0) { + // Need to lookup the connection by port, filter the port, and forward to the user + TSocketManager& Manager = m_pZNC->GetManager(); + + for (unsigned int a = 0; a < Manager.size(); a++) { + CDCCBounce* pSock = (CDCCBounce*) Manager[a]; + + if ((pSock) && (strncasecmp(pSock->GetSockName().c_str(), "DCC::", 5) == 0)) { + if (pSock->GetUserPort() == atoi(CUtils::Token(sMessage, 3).c_str())) { + PutUser(":" + sNickMask + " PRIVMSG " + GetNick() + " :\001DCC " + sType + " " + sFile + " " + CUtils::ToString(pSock->GetLocalPort()) + " " + CUtils::Token(sMessage, 4) + "\001"); + } + } + } + } + + return true; + } + + return false; +} + +bool CIRCSock::OnPrivNotice(const string& sNickMask, string& sMessage) { +#ifdef _MODULES + if (m_pUser->GetModules().OnPrivNotice(sNickMask, sMessage)) { + return true; + } +#endif + if (!m_pUserSock) { + // If the user is detached, add to the buffer + m_QueryBuffer.AddLine(":" + sNickMask + " NOTICE ", " :" + sMessage); + } + + return false; +} + +bool CIRCSock::OnPrivMsg(const string& sNickMask, string& sMessage) { +#ifdef _MODULES + if (m_pUser->GetModules().OnPrivMsg(sNickMask, sMessage)) { + return true; + } +#endif + if (!m_pUserSock) { + // If the user is detached, add to the buffer + m_QueryBuffer.AddLine(":" + sNickMask + " PRIVMSG ", " :" + sMessage); + } + + return false; +} + +bool CIRCSock::OnChanCTCP(const string& sNickMask, const string& sChan, string& sMessage) { + CChan* pChan = m_pUser->FindChan(sChan); + if (pChan) { +#ifdef _MODULES + if (m_pUser->GetModules().OnChanCTCP(sNickMask, *pChan, sMessage)) { + return true; + } +#endif + } + + return false; +} + +bool CIRCSock::OnChanNotice(const string& sNickMask, const string& sChan, string& sMessage) { + CChan* pChan = m_pUser->FindChan(sChan); + if (pChan) { +#ifdef _MODULES + if (m_pUser->GetModules().OnChanNotice(sNickMask, *pChan, sMessage)) { + return true; + } +#endif + if ((pChan->KeepBuffer()) || (!m_pUserSock)) { + pChan->AddBuffer(":" + sNickMask + " NOTICE " + sChan + " :" + sMessage); + } + } + + return false; +} + +bool CIRCSock::OnChanMsg(const string& sNickMask, const string& sChan, string& sMessage) { + CChan* pChan = m_pUser->FindChan(sChan); + if (pChan) { +#ifdef _MODULES + if (m_pUser->GetModules().OnChanMsg(sNickMask, *pChan, sMessage)) { + return true; + } +#endif + if ((pChan->KeepBuffer()) || (!m_pUserSock)) { + pChan->AddBuffer(":" + sNickMask + " PRIVMSG " + sChan + " :" + sMessage); + } + } + + return false; +} + +void CIRCSock::UserConnected(CUserSock* pUserSock) { + if (m_pUserSock) { + m_pUserSock->BouncedOff(); + } + + m_pUserSock = pUserSock; + + if (m_RawBuffer.IsEmpty()) { + PutUser(":irc.znc.com 001 " + m_pUserSock->GetNick() + " :- Welcome to ZNC -"); + } else { + unsigned int uIdx = 0; + string sLine; + + while (m_RawBuffer.GetLine(GetNick(), sLine, uIdx++)) { + PutUser(sLine); + } + } + + PutServ("MOTD"); + + const vector& vChans = m_pUser->GetChans(); + for (unsigned int a = 0; a < vChans.size(); a++) { + if (vChans[a]->IsOn()) { + PutUser(":" + m_Nick.GetNickMask() + " JOIN :" + vChans[a]->GetName()); + PutServ("NAMES " + vChans[a]->GetName()); + } + } + + //PutUser(""); + + string sBufLine; + while (m_QueryBuffer.GetNextLine(GetNick(), sBufLine)) { + PutUser(sBufLine); + } +} + +void CIRCSock::UserDisconnected() { + m_pUserSock = NULL; +} + +void CIRCSock::PutServ(const string& sLine) { + DEBUG_ONLY(cout << GetSockName() << " -> [" << sLine << "]" << endl); + Write(sLine + "\r\n"); +} + +void CIRCSock::PutUser(const string& sLine) { + if (m_pUserSock) { + m_pUserSock->PutServ(sLine); + } +} + +void CIRCSock::PutStatus(const string& sLine) { + if (m_pUserSock) { + m_pUserSock->PutStatus(sLine); + } +} + +void CIRCSock::SetNick(const string& sNick) { + m_Nick.SetNick(sNick); + + if (m_pUserSock) { + m_pUserSock->SetNick(sNick); + } +} + +void CIRCSock::Connected() { + DEBUG_ONLY(cout << GetSockName() << " == Connected()" << endl); + + CUserSock* pUserSock = (CUserSock*) m_pZNC->FindSockByName("USR::" + m_pUser->GetUserName()); + + if (pUserSock) { + m_pUserSock = pUserSock; + pUserSock->IRCConnected(this); + } + + if (!m_sPass.empty()) { + PutServ("PASS " + m_sPass); + } + + PutServ("NICK " + m_pUser->GetNick()); + PutServ("USER " + m_pUser->GetIdent() + " \"" + m_pUser->GetIdent() + "\" \"" + m_pUser->GetIdent() + "\" :" + m_pUser->GetRealName()); +} + +void CIRCSock::Disconnected() { + DEBUG_ONLY(cout << GetSockName() << " == Disconnected()" << endl); + m_pUser->PutStatus("Disconnected from IRC. Reconnecting..."); + +#ifdef _MODULES + m_pUser->GetModules().OnIRCDisconnected(); +#endif + + const vector& vChans = m_pUser->GetChans(); + for (unsigned int a = 0; a < vChans.size(); a++) { + vChans[a]->SetIsOn(false); + } + + if (m_pUserSock) { + m_pUserSock->IRCDisconnected(); + m_pUserSock = NULL; + } +} + +void CIRCSock::SockError(int iErrno) { + DEBUG_ONLY(cout << GetSockName() << " == SockError(" << iErrno << ")" << endl); + m_pUser->PutStatus("Disconnected from IRC. Reconnecting..."); +} + +void CIRCSock::Timeout() { + DEBUG_ONLY(cout << GetSockName() << " == Timeout()" << endl); + m_pUser->PutStatus("IRC connection timed out. Reconnecting..."); +} + +void CIRCSock::ConnectionRefused() { + DEBUG_ONLY(cout << GetSockName() << " == ConnectionRefused()" << endl); + m_pUser->PutStatus("Connection Refused. Reconnecting..."); +} + diff --git a/IRCSock.h b/IRCSock.h new file mode 100644 index 00000000..302f4951 --- /dev/null +++ b/IRCSock.h @@ -0,0 +1,72 @@ +#ifndef _IRCSOCK_H +#define _IRCSOCK_H + +#include "main.h" +#include "User.h" +#include "Chan.h" +#include "Buffer.h" + +// Forward Declarations +class CZNC; +class CUserSock; +// !Forward Declarations + +class CIRCSock : public Csock { +public: + CIRCSock(CZNC* pZNC, CUser* pUser); + virtual ~CIRCSock(); + + // Message Handlers + bool OnCTCPReply(const string& sNickMask, string& sMessage); + bool OnPrivCTCP(const string& sNickMask, string& sMessage); + bool OnChanCTCP(const string& sNickMask, const string& sChan, string& sMessage); + bool OnPrivMsg(const string& sNickMask, string& sMessage); + bool OnChanMsg(const string& sNickMask, const string& sChan, string& sMessage); + bool OnPrivNotice(const string& sNickMask, string& sMessage); + bool OnChanNotice(const string& sNickMask, const string& sChan, string& sMessage); + // !Message Handlers + + virtual void ReadLine(const string& sData); + virtual void Connected(); + virtual void Disconnected(); + virtual void ConnectionRefused(); + virtual void SockError(int iErrno); + virtual void Timeout(); + + void KeepNick(); + bool IsUserAttached() { return (m_pUserSock != NULL); } + void UserConnected(CUserSock* pUserSock); + void UserDisconnected(); + void PutServ(const string& sLine); + void PutUser(const string& sLine); + void PutStatus(const string& sLine); + + // Setters + void SetPass(const string& s) { m_sPass = s; } + // !Setters + + // Getters + string GetNickMask() const { + return m_Nick.GetNickMask(); + } + const string& GetNick() const { return m_Nick.GetNick(); } + const string& GetPass() const { return m_sPass; } + // !Getters +private: + void SetNick(const string& sNick); +protected: + bool m_bISpoofReleased; + bool m_bAuthed; + bool m_bKeepNick; + CZNC* m_pZNC; + CUser* m_pUser; + CNick m_Nick; + string m_sPass; + CBuffer m_RawBuffer; + CUserSock* m_pUserSock; + map m_msChans; + unsigned int m_uQueryBufferCount; + CBuffer m_QueryBuffer; +}; + +#endif // !_IRCSOCK_H diff --git a/Makefile b/Makefile new file mode 100644 index 00000000..14f043b0 --- /dev/null +++ b/Makefile @@ -0,0 +1,31 @@ +CXX=g++ +TARGET=linux-gnu +MACHINE=i686 +CXXFLAGS=-s -O2 -fomit-frame-pointer -D_MODULES -Wall -D_GNU_SOURCE -DHAVE_LIBSSL +#CXXFLAGS=-Wall -ggdb -D_DEBUG -D_MODULES -D_GNU_SOURCE -DHAVE_LIBSSL +INCLUDES= +LIBS=-ldl -lssl -rdynamic + +OBJS=main.o znc.o User.o IRCSock.o UserSock.o DCCBounce.o DCCSock.o Chan.o Nick.o Server.o Modules.o md5.o Buffer.o Utils.o +SRCS=main.cpp znc.cpp User.cpp IRCSock.cpp UserSock.cpp DCCBounce.cpp DCCSock.cpp Chan.cpp Nick.cpp Server.cpp Modules.cpp md5.cpp Buffer.cpp Utils.cpp + +all: znc + +depend:: + cat /dev/null >.depend + g++ -M $(CXXFLAGS) $(SRCS) $(INCLUDES) >.depend + +znc: $(OBJS) + $(CXX) $(CXXFLAGS) $(INCLUDES) -o $@ $(LIBS) $(OBJS) + +znc-static: $(OBJS) + $(CXX) $(CXXFLAGS) -static -o $@ $(INCLUDES) $(OBJS) $(LIBS) + strip $@ + +clean: + rm -rf znc znc-static *.o core core.* + +include .depend + +.cpp.o: + $(CXX) $(CXXFLAGS) $(INCLUDES) -c -o $*.o $*.cpp diff --git a/Modules.cpp b/Modules.cpp new file mode 100644 index 00000000..2c05fa0e --- /dev/null +++ b/Modules.cpp @@ -0,0 +1,594 @@ +#include "main.h" +#include "Modules.h" +#include "Utils.h" +#include "User.h" +#include "Nick.h" +#include "Chan.h" + +/////////////////// Timer /////////////////// +CTimer::CTimer(CModule* pModule, unsigned int uInterval, unsigned int uCycles, const string& sLabel, const string& sDescription) : CCron() { + SetName(sLabel); + m_sDescription = sDescription; + m_pModule = pModule; + + if (uCycles) { + StartMaxCycles(uInterval, uCycles); + } else { + Start(uInterval); + } +} + +CTimer::~CTimer() { + m_pModule->UnlinkTimer(this); +} + +void CTimer::SetModule(CModule* p) { m_pModule = p; } +void CTimer::SetDescription(const string& s) { m_sDescription = s; } +CModule* CTimer::GetModule() const { return m_pModule; } +const string& CTimer::GetDescription() const { return m_sDescription; } +/////////////////// !Timer /////////////////// + +CModule::CModule(void* pDLL, CUser* pUser, const string& sModName) { + m_pDLL = pDLL; + m_pManager = pUser->GetManager(); + m_pUser = pUser; + m_sModName = sModName; +} + +CModule::~CModule() { + while (m_vTimers.size()) { + RemTimer(m_vTimers[0]->GetName()); + } +} + +bool CModule::AddTimer(CTimer* pTimer) { + if ((!pTimer) || (FindTimer(pTimer->GetName()))) { + delete pTimer; + return false; + } + + m_pManager->AddCron(pTimer); + m_vTimers.push_back(pTimer); + return true; +} + +bool CModule::RemTimer(const string& sLabel) { + for (unsigned int a = 0; a < m_vTimers.size(); a++) { + CTimer* pTimer = m_vTimers[a]; + + if (strcasecmp(pTimer->GetName().c_str(), sLabel.c_str()) == 0) { + m_vTimers.erase(m_vTimers.begin() +a); + m_pManager->DelCronByAddr(pTimer); + return true; + } + } + + return false; +} + +bool CModule::UnlinkTimer(CTimer* pTimer) { + for (unsigned int a = 0; a < m_vTimers.size(); a++) { + if (pTimer == m_vTimers[a]) { + m_vTimers.erase(m_vTimers.begin() +a); + return true; + } + } + + return false; +} + +CTimer* CModule::FindTimer(const string& sLabel) { + for (unsigned int a = 0; a < m_vTimers.size(); a++) { + CTimer* pTimer = m_vTimers[a]; + if (strcasecmp(pTimer->GetName().c_str(), sLabel.c_str()) == 0) { + return pTimer; + } + } + + return NULL; +} + +void CModule::ListTimers() { + if (!m_vTimers.size()) { + PutModule("You have no timers running."); + return; + } + + CTable Table; + Table.AddColumn("Name"); + Table.AddColumn("Secs"); + Table.AddColumn("Cycles"); + Table.AddColumn("Description"); + + for (unsigned int a = 0; a < m_vTimers.size(); a++) { + CTimer* pTimer = (CTimer*) m_vTimers[a]; + unsigned int uCycles = pTimer->GetCyclesLeft(); + + Table.AddRow(); + Table.SetCell("Name", pTimer->GetName()); + Table.SetCell("Secs", CUtils::ToString(pTimer->GetInterval())); + Table.SetCell("Cycles", ((uCycles) ? CUtils::ToString(uCycles) : "INF")); + Table.SetCell("Description", pTimer->GetDescription()); + } + + if (Table.size()) { + unsigned int uTableIdx = 0; + string sLine; + + while (Table.GetLine(uTableIdx++, sLine)) { + PutModule(sLine); + } + } +} + +const string& CModule::GetModName() { return m_sModName; } +string CModule::GetModNick() { return ((m_pUser) ? m_pUser->GetStatusPrefix() : "*") + m_sModName; } + +string CModule::GetDescription() { return "Unknown"; } + +bool CModule::OnLoad(const string& sArgs) { return true; } +bool CModule::OnBoot() { return true; } +void CModule::OnUserAttached() {} +void CModule::OnUserDetached() {} +void CModule::OnIRCDisconnected() {} +void CModule::OnIRCConnected() {} + +bool CModule::OnDCCUserSend(const CNick& RemoteNick, unsigned long uLongIP, unsigned short uPort, const string& sFile, unsigned long uFileSize) { return false; } + +void CModule::OnOp(const CNick& OpNick, const CNick& Nick, const CChan& Channel, bool bNoChange) {} +void CModule::OnDeop(const CNick& OpNick, const CNick& Nick, const CChan& Channel, bool bNoChange) {} +void CModule::OnVoice(const CNick& OpNick, const CNick& Nick, const CChan& Channel, bool bNoChange) {} +void CModule::OnDevoice(const CNick& OpNick, const CNick& Nick, const CChan& Channel, bool bNoChange) {} +void CModule::OnRawMode(const CNick& OpNick, const CChan& Channel, const string& sModes, const string& sArgs) {} + +bool CModule::OnUserRaw(string& sLine) { return false; } +bool CModule::OnRaw(string& sLine) { return false; } + +bool CModule::OnStatusCommand(const string& sCommand) { return false; } +void CModule::OnModCommand(const string& sCommand) {} +void CModule::OnModNotice(const string& sMessage) {} +void CModule::OnModCTCP(const string& sMessage) {} + +void CModule::OnQuit(const CNick& Nick, const string& sMessage) {} +void CModule::OnNick(const CNick& Nick, const string& sNewNick) {} +void CModule::OnKick(const CNick& Nick, const string& sKickedNick, const CChan& Channel, const string& sMessage) {} +void CModule::OnJoin(const CNick& Nick, const CChan& Channel) {} +void CModule::OnPart(const CNick& Nick, const CChan& Channel) {} + +bool CModule::OnUserCTCPReply(const CNick& Nick, string& sMessage) { return false; } +bool CModule::OnCTCPReply(const CNick& Nick, string& sMessage) { return false; } +bool CModule::OnUserCTCP(const string& sTarget, string& sMessage) { return false; } +bool CModule::OnPrivCTCP(const CNick& Nick, string& sMessage) { return false; } +bool CModule::OnChanCTCP(const CNick& Nick, const CChan& Channel, string& sMessage) { return false; } +bool CModule::OnUserMsg(const string& sTarget, string& sMessage) { return false; } +bool CModule::OnPrivMsg(const CNick& Nick, string& sMessage) { return false; } +bool CModule::OnChanMsg(const CNick& Nick, const CChan& Channel, string& sMessage) { return false; } +bool CModule::OnUserNotice(const string& sTarget, string& sMessage) { return false; } +bool CModule::OnPrivNotice(const CNick& Nick, string& sMessage) { return false; } +bool CModule::OnChanNotice(const CNick& Nick, const CChan& Channel, string& sMessage) { return false; } + +void* CModule::GetDLL() { return m_pDLL; } +bool CModule::PutIRC(const string& sLine) { + return (m_pUser) ? m_pUser->PutIRC(sLine) : false; +} +bool CModule::PutUser(const string& sLine) { + return (m_pUser) ? m_pUser->PutUser(sLine) : false; +} +bool CModule::PutStatus(const string& sLine) { + return (m_pUser) ? m_pUser->PutStatus(sLine) : false; +} +bool CModule::PutModule(const string& sLine, const string& sIdent, const string& sHost) { + return (m_pUser) ? m_pUser->PutUser(":" + GetModNick() + "!" + sIdent + "@" + sHost + " PRIVMSG " + m_pUser->GetCurNick() + " :" + sLine) : false; +} +bool CModule::PutModNotice(const string& sLine, const string& sIdent, const string& sHost) { + return (m_pUser) ? m_pUser->PutUser(":" + GetModNick() + "!" + sIdent + "@" + sHost + " NOTICE " + m_pUser->GetCurNick() + " :" + sLine) : false; +} + +CModules::CModules() {} +CModules::~CModules() {} + +void CModules::UnloadAll() { + while (size()) { + string sRetMsg; + string sModName = (*this)[0]->GetModName(); + UnloadModule(sModName, sRetMsg); + } +} + +void CModules::OnIRCConnected() { + for (unsigned int a = 0; a < size(); a++) { + (*this)[a]->OnIRCConnected(); + } +} + +bool CModules::OnLoad(const string& sArgs) { + for (unsigned int a = 0; a < size(); a++) { + if (!(*this)[a]->OnLoad(sArgs)) { + return false; + } + } + + return true; +} + +bool CModules::OnBoot() { + for (unsigned int a = 0; a < size(); a++) { + if (!(*this)[a]->OnBoot()) { + return false; + } + } + + return true; +} + +void CModules::OnUserAttached() { + for (unsigned int a = 0; a < size(); a++) { + (*this)[a]->OnUserAttached(); + } +} + +void CModules::OnUserDetached() { + for (unsigned int a = 0; a < size(); a++) { + (*this)[a]->OnUserDetached(); + } +} + +void CModules::OnIRCDisconnected() { + for (unsigned int a = 0; a < size(); a++) { + (*this)[a]->OnIRCDisconnected(); + } +} + +bool CModules::OnDCCUserSend(const CNick& RemoteNick, unsigned long uLongIP, unsigned short uPort, const string& sFile, unsigned long uFileSize) { + for (unsigned int a = 0; a < size(); a++) { + if ((*this)[a]->OnDCCUserSend(RemoteNick, uLongIP, uPort, sFile, uFileSize)) { + return true; + } + } + + return false; +} + +void CModules::OnOp(const CNick& OpNick, const CNick& Nick, const CChan& Channel, bool bNoChange) { + for (unsigned int a = 0; a < size(); a++) { + (*this)[a]->OnOp(OpNick, Nick, Channel, bNoChange); + } +} + +void CModules::OnDeop(const CNick& OpNick, const CNick& Nick, const CChan& Channel, bool bNoChange) { + for (unsigned int a = 0; a < size(); a++) { + (*this)[a]->OnDeop(OpNick, Nick, Channel, bNoChange); + } +} + +void CModules::OnVoice(const CNick& OpNick, const CNick& Nick, const CChan& Channel, bool bNoChange) { + for (unsigned int a = 0; a < size(); a++) { + (*this)[a]->OnVoice(OpNick, Nick, Channel, bNoChange); + } +} + +void CModules::OnDevoice(const CNick& OpNick, const CNick& Nick, const CChan& Channel, bool bNoChange) { + for (unsigned int a = 0; a < size(); a++) { + (*this)[a]->OnDevoice(OpNick, Nick, Channel, bNoChange); + } +} + +void CModules::OnRawMode(const CNick& OpNick, const CChan& Channel, const string& sModes, const string& sArgs) { + for (unsigned int a = 0; a < size(); a++) { + (*this)[a]->OnRawMode(OpNick, Channel, sModes, sArgs); + } +} + +bool CModules::OnRaw(string& sLine) { + for (unsigned int a = 0; a < size(); a++) { + if ((*this)[a]->OnRaw(sLine)) { + return true; + } + } + + return false; +} + +bool CModules::OnUserRaw(string& sLine) { + for (unsigned int a = 0; a < size(); a++) { + if ((*this)[a]->OnUserRaw(sLine)) { + return true; + } + } + + return false; +} + +void CModules::OnQuit(const CNick& Nick, const string& sMessage) { + for (unsigned int a = 0; a < size(); a++) { + (*this)[a]->OnQuit(Nick, sMessage); + } +} + +void CModules::OnNick(const CNick& Nick, const string& sNewNick) { + for (unsigned int a = 0; a < size(); a++) { + (*this)[a]->OnNick(Nick, sNewNick); + } +} + +void CModules::OnKick(const CNick& Nick, const string& sKickedNick, const CChan& Channel, const string& sMessage) { + for (unsigned int a = 0; a < size(); a++) { + (*this)[a]->OnKick(Nick, sKickedNick, Channel, sMessage); + } +} + +void CModules::OnJoin(const CNick& Nick, const CChan& Channel) { + for (unsigned int a = 0; a < size(); a++) { + (*this)[a]->OnJoin(Nick, Channel); + } +} + +void CModules::OnPart(const CNick& Nick, const CChan& Channel) { + for (unsigned int a = 0; a < size(); a++) { + (*this)[a]->OnPart(Nick, Channel); + } +} + +bool CModules::OnUserCTCP(const string& sTarget, string& sMessage) { + for (unsigned int a = 0; a < size(); a++) { + if ((*this)[a]->OnUserCTCP(sTarget, sMessage)) { + return true; + } + } + + return false; +} + +bool CModules::OnUserCTCPReply(const CNick& Nick, string& sMessage) { + for (unsigned int a = 0; a < size(); a++) { + if ((*this)[a]->OnUserCTCPReply(Nick, sMessage)) { + return true; + } + } + + return false; +} + +bool CModules::OnCTCPReply(const CNick& Nick, string& sMessage) { + for (unsigned int a = 0; a < size(); a++) { + if ((*this)[a]->OnCTCPReply(Nick, sMessage)) { + return true; + } + } + + return false; +} + +bool CModules::OnPrivCTCP(const CNick& Nick, string& sMessage) { + for (unsigned int a = 0; a < size(); a++) { + if ((*this)[a]->OnPrivCTCP(Nick, sMessage)) { + return true; + } + } + + return false; +} + +bool CModules::OnChanCTCP(const CNick& Nick, const CChan& Channel, string& sMessage) { + for (unsigned int a = 0; a < size(); a++) { + if ((*this)[a]->OnChanCTCP(Nick, Channel, sMessage)) { + return true; + } + } + + return false; +} + +bool CModules::OnUserMsg(const string& sTarget, string& sMessage) { + for (unsigned int a = 0; a < size(); a++) { + if ((*this)[a]->OnUserMsg(sTarget, sMessage)) { + return true; + } + } + + return false; +} + +bool CModules::OnPrivMsg(const CNick& Nick, string& sMessage) { + for (unsigned int a = 0; a < size(); a++) { + if ((*this)[a]->OnPrivMsg(Nick, sMessage)) { + return true; + } + } + + return false; +} + +bool CModules::OnChanMsg(const CNick& Nick, const CChan& Channel, string& sMessage) { + for (unsigned int a = 0; a < size(); a++) { + if ((*this)[a]->OnChanMsg(Nick, Channel, sMessage)) { + return true; + } + } + + return false; +} + +bool CModules::OnUserNotice(const string& sTarget, string& sMessage) { + for (unsigned int a = 0; a < size(); a++) { + if ((*this)[a]->OnUserNotice(sTarget, sMessage)) { + return true; + } + } + + return false; +} + +bool CModules::OnPrivNotice(const CNick& Nick, string& sMessage) { + for (unsigned int a = 0; a < size(); a++) { + if ((*this)[a]->OnPrivNotice(Nick, sMessage)) { + return true; + } + } + + return false; +} + +bool CModules::OnChanNotice(const CNick& Nick, const CChan& Channel, string& sMessage) { + for (unsigned int a = 0; a < size(); a++) { + if ((*this)[a]->OnChanNotice(Nick, Channel, sMessage)) { + return true; + } + } + + return false; +} + +bool CModules::OnStatusCommand(const string& sCommand) { + for (unsigned int a = 0; a < size(); a++) { + if ((*this)[a]->OnStatusCommand(sCommand)) { + return true; + } + } + + return false; +} + +void CModules::OnModCommand(const string& sCommand) { + for (unsigned int a = 0; a < size(); a++) { + (*this)[a]->OnModCommand(sCommand); + } +} + +void CModules::OnModNotice(const string& sMessage) { + for (unsigned int a = 0; a < size(); a++) { + (*this)[a]->OnModNotice(sMessage); + } +} + +void CModules::OnModCTCP(const string& sMessage) { + for (unsigned int a = 0; a < size(); a++) { + (*this)[a]->OnModCTCP(sMessage); + } +} + +CModule* CModules::FindModule(const string& sModule) { + for (unsigned int a = 0; a < size(); a++) { + if (strcasecmp(sModule.c_str(), (*this)[a]->GetModName().c_str()) == 0) { + return (*this)[a]; + } + } + + return NULL; +} + +bool CModules::LoadModule(const string& sModule, const string& sArgs, CUser* pUser, const string& sPath, string& sRetMsg) { + sRetMsg = ""; + + if (!pUser) { + sRetMsg = "Unable to load module [" + sModule + "] Internal Error 1."; + return false; + } + + for (unsigned int a = 0; a < sModule.length(); a++) { + if (((sModule[a] < '0') || (sModule[a] > '9')) && ((sModule[a] < 'a') || (sModule[a] > 'z')) && ((sModule[a] < 'A') || (sModule[a] > 'Z')) && (sModule[a] != '_')) { + sRetMsg = "Unable to load module [" + sModule + "] module names can only be letters, numbers, or underscores."; + return false; + } + } + + if (FindModule(sModule) != NULL) { + sRetMsg = "Module [" + sModule + "] already loaded."; + return false; + } + + void* p = dlopen((pUser->GetModPath() + "/" + sModule + ".so").c_str(), RTLD_LAZY); + + if (!p) { + sRetMsg = "Unable to load module [" + sModule + "] [" + dlerror() + "]"; + return false; + } + + typedef double (*fpp)(); + fpp Version = (fpp) dlsym(p, "GetVersion"); + if (!Version) { + dlclose(p); + sRetMsg = "Could not find Version() in module [" + sModule + "]"; + return false; + } + + if (CModule::GetVersion() != Version()) { + dlclose(p); + sRetMsg = "Version mismatch in module [" + sModule + "] - You need to recompile your modules using this version of znc."; + return false; + } + + typedef CModule* (*fp)(void*, CUser* pUser, const string& sModName); + fp Load = (fp) dlsym(p, "Load"); + + if (!Load) { + dlclose(p); + sRetMsg = "Could not find Load() in module [" + sModule + "]"; + return false; + } + + CModule* pModule = Load(p, pUser, sModule); + push_back(pModule); + + if (!pModule->OnLoad(sArgs)) { + UnloadModule(sModule, sRetMsg); + sRetMsg = "Module [" + sModule + "] aborted."; + return false; + } + + sRetMsg = "Loaded module [" + sModule + "]"; + return true; +} + +bool CModules::UnloadModule(const string& sModule, string& sRetMsg) { + CModule* pModule = FindModule(sModule); + sRetMsg = ""; + + if (!pModule) { + sRetMsg = "Module [" + sModule + "] not loaded."; + return false; + } + + void* p = pModule->GetDLL(); + + if (p) { + typedef void (*fp)(CModule*); + fp Unload = (fp)dlsym(p, "Unload"); + + if (Unload) { + Unload(pModule); + + for (iterator it = begin(); it != end(); it++) { + if (*it == pModule) { + erase(it); + break; + } + } + + dlclose(p); + sRetMsg = "Module [" + sModule + "] unloaded"; + + return true; + } else { + sRetMsg = "Unable to unload module [" + sModule + "] could not find Unload()"; + return false; + } + } + + sRetMsg = "Unable to unload module [" + sModule + "]"; + return false; +} + +bool CModules::ReloadModule(const string& sModule, const string& sArgs, CUser* pUser, const string& sPath, string& sRetMsg) { + sRetMsg = ""; + if (!UnloadModule(sModule, sRetMsg)) { + return false; + } + + if (!LoadModule(sModule, sArgs, pUser, sPath, sRetMsg)) { + return false; + } + + sRetMsg = "Reloaded module [" + sModule + "]"; + return true; +} diff --git a/Modules.h b/Modules.h new file mode 100644 index 00000000..7c1eadcd --- /dev/null +++ b/Modules.h @@ -0,0 +1,174 @@ +#ifndef _MODULES_H +#define _MODULES_H + +#include +#include +#include +using std::vector; +using std::string; + +#define VERSION 0.022 +#define MODULEDEFS(CLASS) extern "C" { CModule* Load(void* p, CUser* pUser, const string& sModName); void Unload(CModule* pMod); double GetVersion(); } double GetVersion() { return VERSION; } CModule* Load(void* p, CUser* pUser, const string& sModName) { return new CLASS(p, pUser, sModName); } void Unload(CModule* pMod) { if (pMod) { delete pMod; } } +#define MODCONSTRUCTOR(CLASS) CLASS(void *pDLL, CUser* pUser, const string& sModName) : CModule(pDLL, pUser, sModName) + +// Forward Declarations +class CUser; +class CNick; +class CChan; +class Csock; +class CModule; +template class TSocketManager; +// !Forward Declarations + +class CTimer : public CCron { +public: + CTimer(CModule* pModule, unsigned int uInterval, unsigned int uCycles, const string& sLabel, const string& sDescription); + + virtual ~CTimer(); + + // Setters + void SetModule(CModule* p); + void SetDescription(const string& s); + // !Setters + + // Getters + CModule* GetModule() const; + const string& GetDescription() const; + // !Getters +private: +protected: + CModule* m_pModule; + string m_sDescription; +}; + +class CModule { +public: + CModule(void* pDLL, CUser* pUser, const string& sModName); + virtual ~CModule(); + + virtual string GetDescription(); + + virtual bool OnLoad(const string& sArgs); + virtual bool OnBoot(); + virtual void OnUserAttached(); + virtual void OnUserDetached(); + virtual void OnIRCDisconnected(); + virtual void OnIRCConnected(); + + virtual bool OnDCCUserSend(const CNick& RemoteNick, unsigned long uLongIP, unsigned short uPort, const string& sFile, unsigned long uFileSize); + + virtual void OnOp(const CNick& OpNick, const CNick& Nick, const CChan& Channel, bool bNoChange); + virtual void OnDeop(const CNick& OpNick, const CNick& Nick, const CChan& Channel, bool bNoChange); + virtual void OnVoice(const CNick& OpNick, const CNick& Nick, const CChan& Channel, bool bNoChange); + virtual void OnDevoice(const CNick& OpNick, const CNick& Nick, const CChan& Channel, bool bNoChange); + virtual void OnRawMode(const CNick& OpNick, const CChan& Channel, const string& sModes, const string& sArgs); + + virtual bool OnUserRaw(string& sLine); + virtual bool OnRaw(string& sLine); + + virtual bool OnStatusCommand(const string& sCommand); + virtual void OnModCommand(const string& sCommand); + virtual void OnModNotice(const string& sMessage); + virtual void OnModCTCP(const string& sMessage); + + virtual void OnQuit(const CNick& Nick, const string& sMessage); + virtual void OnNick(const CNick& Nick, const string& sNewNick); + virtual void OnKick(const CNick& Nick, const string& sOpNick, const CChan& Channel, const string& sMessage); + virtual void OnJoin(const CNick& Nick, const CChan& Channel); + virtual void OnPart(const CNick& Nick, const CChan& Channel); + + virtual bool OnUserCTCPReply(const CNick& Nick, string& sMessage); + virtual bool OnCTCPReply(const CNick& Nick, string& sMessage); + virtual bool OnUserCTCP(const string& sTarget, string& sMessage); + virtual bool OnPrivCTCP(const CNick& Nick, string& sMessage); + virtual bool OnChanCTCP(const CNick& Nick, const CChan& Channel, string& sMessage); + virtual bool OnUserMsg(const string& sTarget, string& sMessage); + virtual bool OnPrivMsg(const CNick& Nick, string& sMessage); + virtual bool OnChanMsg(const CNick& Nick, const CChan& Channel, string& sMessage); + virtual bool OnUserNotice(const string& sTarget, string& sMessage); + virtual bool OnPrivNotice(const CNick& Nick, string& sMessage); + virtual bool OnChanNotice(const CNick& Nick, const CChan& Channel, string& sMessage); + + void * GetDLL(); + static double GetVersion() { return VERSION; } + + virtual bool PutIRC(const string& sLine); + virtual bool PutUser(const string& sLine); + virtual bool PutStatus(const string& sLine); + virtual bool PutModule(const string& sLine, const string& sIdent = "znc", const string& sHost = "znc.com"); + virtual bool PutModNotice(const string& sLine, const string& sIdent = "znc", const string& sHost = "znc.com"); + + const string& GetModName(); + string GetModNick(); + + // Timer stuff + bool AddTimer(CTimer* pTimer); + bool RemTimer(const string& sLabel); + bool UnlinkTimer(CTimer* pTimer); + CTimer* FindTimer(const string& sLabel); + virtual void ListTimers(); + // !Timer stuff + +protected: + vector m_vTimers; + void* m_pDLL; + TSocketManager* m_pManager; + CUser* m_pUser; + string m_sModName; +}; + +class CModules : public vector { +public: + CModules(); + virtual ~CModules(); + + void UnloadAll(); + + virtual bool OnLoad(const string& sArgs); + virtual bool OnBoot(); + virtual void OnUserAttached(); + virtual void OnUserDetached(); + virtual void OnIRCDisconnected(); + virtual void OnIRCConnected(); + + virtual bool OnDCCUserSend(const CNick& RemoteNick, unsigned long uLongIP, unsigned short uPort, const string& sFile, unsigned long uFileSize); + + virtual void OnOp(const CNick& OpNick, const CNick& Nick, const CChan& Channel, bool bNoChange); + virtual void OnDeop(const CNick& OpNick, const CNick& Nick, const CChan& Channel, bool bNoChange); + virtual void OnVoice(const CNick& OpNick, const CNick& Nick, const CChan& Channel, bool bNoChange); + virtual void OnDevoice(const CNick& OpNick, const CNick& Nick, const CChan& Channel, bool bNoChange); + virtual void OnRawMode(const CNick& OpNick, const CChan& Channel, const string& sModes, const string& sArgs); + + virtual bool OnUserRaw(string& sLine); + virtual bool OnRaw(string& sLine); + + virtual bool OnStatusCommand(const string& sCommand); + virtual void OnModCommand(const string& sCommand); + virtual void OnModNotice(const string& sMessage); + virtual void OnModCTCP(const string& sMessage); + + virtual void OnQuit(const CNick& Nick, const string& sMessage); + virtual void OnNick(const CNick& Nick, const string& sNewNick); + virtual void OnKick(const CNick& Nick, const string& sOpNick, const CChan& Channel, const string& sMessage); + virtual void OnJoin(const CNick& Nick, const CChan& Channel); + virtual void OnPart(const CNick& Nick, const CChan& Channel); + + virtual bool OnUserCTCPReply(const CNick& Nick, string& sMessage); + virtual bool OnCTCPReply(const CNick& Nick, string& sMessage); + virtual bool OnUserCTCP(const string& sTarget, string& sMessage); + virtual bool OnPrivCTCP(const CNick& Nick, string& sMessage); + virtual bool OnChanCTCP(const CNick& Nick, const CChan& Channel, string& sMessage); + virtual bool OnUserMsg(const string& sTarget, string& sMessage); + virtual bool OnPrivMsg(const CNick& Nick, string& sMessage); + virtual bool OnChanMsg(const CNick& Nick, const CChan& Channel, string& sMessage); + virtual bool OnUserNotice(const string& sTarget, string& sMessage); + virtual bool OnPrivNotice(const CNick& Nick, string& sMessage); + virtual bool OnChanNotice(const CNick& Nick, const CChan& Channel, string& sMessage); + + CModule* FindModule(const string& sModule); + bool LoadModule(const string& sModule, const string& sArgs, CUser* pUser, const string& sPath, string& sRetMsg); + bool UnloadModule(const string& sModule, string& sRetMsg); + bool ReloadModule(const string& sModule, const string& sArgs, CUser* pUser, const string& sPath, string& sRetMsg); +}; + +#endif // !_MODULES_H diff --git a/Nick.cpp b/Nick.cpp new file mode 100644 index 00000000..d29cae7d --- /dev/null +++ b/Nick.cpp @@ -0,0 +1,62 @@ +#include "Nick.h" + +CNick::CNick() { + m_bIsOp = false; + m_bIsVoice = false; +} + +CNick::CNick(const string& sNick) { + Parse(sNick); + m_bIsOp = false; + m_bIsVoice = false; +} + +CNick::~CNick() {} + +void CNick::Parse(const string& sNickMask) { + if (sNickMask.empty()) { + return; + } + + unsigned int uPos = sNickMask.find('!'); + + if (uPos == string::npos) { + m_sNick = sNickMask.substr((sNickMask[0] == ':')); + return; + } + + m_sNick = sNickMask.substr((sNickMask[0] == ':'), uPos); + m_sHost = sNickMask.substr(uPos +1); + + if ((uPos = m_sHost.find('@')) != string::npos) { + m_sIdent = m_sHost.substr(0, uPos); + m_sHost = m_sHost.substr(uPos +1); + } +} + +void CNick::SetNick(const string& s) { m_sNick = s; } +void CNick::SetIdent(const string& s) { m_sIdent = s; } +void CNick::SetHost(const string& s) { m_sHost = s; } +void CNick::SetOp(bool b) { m_bIsOp = b; } +void CNick::SetVoice(bool b) { m_bIsVoice = b; } + +bool CNick::IsOp() const { return m_bIsOp; } +bool CNick::IsVoice() const { return m_bIsVoice; } +const string& CNick::GetNick() const { return m_sNick; } +const string& CNick::GetIdent() const { return m_sIdent; } +const string& CNick::GetHost() const { return m_sHost; } +string CNick::GetNickMask() const { return m_sNick + "!" + m_sIdent + "@" + m_sHost; } + +string CNick::GetHostMask() const { + string sRet = m_sNick; + + if (!m_sIdent.empty()) { + sRet += "!" + m_sIdent; + } + + if (!m_sHost.empty()) { + sRet += "@" + m_sHost; + } + + return (sRet); +} diff --git a/Nick.h b/Nick.h new file mode 100644 index 00000000..b97227bf --- /dev/null +++ b/Nick.h @@ -0,0 +1,44 @@ +#ifndef _NICK_H +#define _NICK_H + +#include +using std::string; + +class CNick +{ +public: + CNick(); + CNick(const string& sNick); + virtual ~CNick(); + + void Parse(const string& sNickMask); + string GetHostMask() const; + + // Setters + void SetNick(const string& s); + void SetIdent(const string& s); + void SetHost(const string& s); + void SetOp(bool b); + void SetVoice(bool b); + // !Setters + + // Getters + bool IsOp() const; + bool IsVoice() const; + const string& GetNick() const; + const string& GetIdent() const; + const string& GetHost() const; + string GetNickMask() const; + // !Getters +private: +protected: + string m_sNick; + string m_sIdent; + string m_sHost; + + bool m_bIsOp; + bool m_bIsVoice; +}; + +#endif // !_NICK_H + diff --git a/Server.cpp b/Server.cpp new file mode 100644 index 00000000..f12b0c19 --- /dev/null +++ b/Server.cpp @@ -0,0 +1,17 @@ +#include "main.h" +#include "Server.h" + +CServer::CServer(const string& sName, unsigned short uPort, const string& sPass, bool bSSL) { + m_sName = sName; + m_uPort = (uPort) ? uPort : 6667; + m_sPass = sPass; + m_bSSL = bSSL; +} + +CServer::~CServer() {} + +const string& CServer::GetName() const { return m_sName; } +unsigned short CServer::GetPort() const { return m_uPort; } +const string& CServer::GetPass() const { return m_sPass; } +bool CServer::IsSSL() const { return m_bSSL; } + diff --git a/Server.h b/Server.h new file mode 100644 index 00000000..802abdb2 --- /dev/null +++ b/Server.h @@ -0,0 +1,23 @@ +#ifndef _SERVER_H +#define _SERVER_H + +#include + +class CServer { +public: + CServer(const string& sName, unsigned short uPort = 6667, const string& sPass = "", bool bSSL = false); + virtual ~CServer(); + + const string& GetName() const; + unsigned short GetPort() const; + const string& GetPass() const; + bool IsSSL() const; +private: +protected: + string m_sName; + unsigned short m_uPort; + string m_sPass; + bool m_bSSL; +}; + +#endif // !_SERVER_H diff --git a/Timers.h b/Timers.h new file mode 100644 index 00000000..22751f3a --- /dev/null +++ b/Timers.h @@ -0,0 +1,49 @@ +#include "main.h" + +class CKeepNickTimer : public CCron { +public: + CKeepNickTimer(CUser* pUser) : CCron() { + m_pUser = pUser; + Start(5); + } + virtual ~CKeepNickTimer() {} + +private: +protected: + virtual void RunJob() { + CIRCSock* pSock = m_pUser->GetIRCSock(); + if (pSock) { + pSock->KeepNick(); + } + } + + CUser* m_pUser; +}; + +class CJoinTimer : public CCron { +public: + CJoinTimer(CUser* pUser) : CCron() { + m_pUser = pUser; + Start(20); + } + virtual ~CJoinTimer() {} + +private: +protected: + virtual void RunJob() { + CIRCSock* pSock = m_pUser->GetIRCSock(); + + if (pSock) { + const vector& vChans = m_pUser->GetChans(); + + for (unsigned int a = 0; a < vChans.size(); a++) { + if (!vChans[a]->IsOn()) { + pSock->PutServ("JOIN " + vChans[a]->GetName() + " " + vChans[a]->GetKey()); + } + } + } + } + + CUser* m_pUser; +}; + diff --git a/User.cpp b/User.cpp new file mode 100644 index 00000000..7de493f1 --- /dev/null +++ b/User.cpp @@ -0,0 +1,397 @@ +#include "main.h" +#include "znc.h" +#include "User.h" +#include "Server.h" +#include "IRCSock.h" +#include "UserSock.h" +#include "DCCBounce.h" +#include "DCCSock.h" +#include "md5.h" +#include "Timers.h" + +CUser::CUser(const string& sUserName, CZNC* pZNC) { + m_sUserName = sUserName; + m_sNick = sUserName; + m_sIdent = sUserName; + m_sRealName = sUserName; + m_uServerIdx = 0; + m_pZNC = pZNC; + m_bPassHashed = false; + m_bUseClientIP = false; + m_bKeepNick = false; + m_bDenyLoadMod = false; + m_sStatusPrefix = "*"; + m_pZNC->GetManager().AddCron(new CKeepNickTimer(this)); + m_pZNC->GetManager().AddCron(new CJoinTimer(this)); +} + +CUser::~CUser() { +#ifdef _MODULES + m_Modules.UnloadAll(); +#endif + for (unsigned int a = 0; a < m_vServers.size(); a++) { + delete m_vServers[a]; + } + + for (unsigned int b = 0; b < m_vChans.size(); b++) { + delete m_vChans[b]; + } +} + +bool CUser::OnBoot() { +#ifdef _MODULES + return m_Modules.OnBoot(); +#endif + return true; +} + +bool CUser::AddAllowedHost(const string& sHostMask) { + if (m_ssAllowedHosts.find(sHostMask) != m_ssAllowedHosts.end()) { + return false; + } + + m_ssAllowedHosts.insert(sHostMask); + return true; +} + +bool CUser::IsHostAllowed(const string& sHostMask) { + for (set::iterator a = m_ssAllowedHosts.begin(); a != m_ssAllowedHosts.end(); a++) { + if (CUtils::WildCmp(*a, sHostMask)) { + return true; + } + } + + return false; +} + +bool CUser::AddChan(CChan* pChan) { + if (!pChan) { + return false; + } + + for (unsigned int a = 0; a < m_vChans.size(); a++) { + if (strcasecmp(m_vChans[a]->GetName().c_str(), pChan->GetName().c_str()) == 0) { + delete pChan; + return false; + } + } + + m_vChans.push_back(pChan); + return true; +} + +bool CUser::AddChan(const string& sName, unsigned int uBufferCount) { + if (sName.empty()) { + return false; + } + + for (unsigned int a = 0; a < m_vChans.size(); a++) { + if (strcasecmp(sName.c_str(), m_vChans[a]->GetName().c_str()) == 0) { + return false; + } + } + + CChan* pChan = new CChan(sName, this, uBufferCount); + m_vChans.push_back(pChan); + return true; +} + +bool CUser::DelChan(const string& sName) { + for (vector::iterator a = m_vChans.begin(); a != m_vChans.end(); a++) { + if (strcasecmp(sName.c_str(), (*a)->GetName().c_str()) == 0) { + m_vChans.erase(a); + return true; + } + } + + return false; +} + +CChan* CUser::FindChan(const string& sName) { + for (unsigned int a = 0; a < m_vChans.size(); a++) { + if (strcasecmp(sName.c_str(), m_vChans[a]->GetName().c_str()) == 0) { + return m_vChans[a]; + } + } + + return NULL; +} + +bool CUser::AddServer(const string& sName) { + if (sName.empty()) { + return false; + } + + bool bSSL = false; + string sLine = sName; + CUtils::Trim(sLine); + + string sHost = CUtils::Token(sLine, 0); + string sPort = CUtils::Token(sLine, 1); + + if (CUtils::Left(sPort, 1) == "+") { + bSSL = true; + CUtils::LeftChomp(sPort); + } + + unsigned short uPort = strtoul(sPort.c_str(), NULL, 10); + string sPass = CUtils::Token(sLine, 2, true); + + return AddServer(sHost, uPort, sPass, bSSL); +} + +bool CUser::AddServer(const string& sName, unsigned short uPort, const string& sPass, bool bSSL) { +#ifndef HAVE_LIBSSL + if (bSSL) { + return false; + } +#endif + if (sName.empty()) { + return false; + } + + if (!uPort) { + uPort = 6667; + } + + CServer* pServer = new CServer(sName, uPort, sPass, bSSL); + m_vServers.push_back(pServer); + + return true; +} + +CServer* CUser::GetNextServer() { + if (m_vServers.empty()) { + return NULL; + } + + if (m_uServerIdx >= m_vServers.size()) { + m_uServerIdx = 0; + } + + return m_vServers[m_uServerIdx++]; // Todo: cycle through these +} + +bool CUser::CheckPass(const string& sPass) { + if (!m_bPassHashed) { + return (sPass == m_sPass); + } + + return (strcasecmp(m_sPass.c_str(), CMD5(sPass)) == 0); +} + +TSocketManager* CUser::GetManager() { + return &m_pZNC->GetManager(); +} + +CZNC* CUser::GetZNC() { + return m_pZNC; +} + +CUserSock* CUser::GetUserSock() { + // Todo: optimize this by saving a pointer to the sock + return (CUserSock*) m_pZNC->GetManager().FindSockByName("USR::" + m_sUserName); +} + +bool CUser::IsUserAttached() { + CUserSock* pUserSock = GetUserSock(); + + if (!pUserSock) { + return false; + } + + return pUserSock->IsAttached(); +} + +CIRCSock* CUser::GetIRCSock() { + // Todo: optimize this by saving a pointer to the sock + return (CIRCSock*) m_pZNC->GetManager().FindSockByName("IRC::" + m_sUserName); +} + +string CUser::GetLocalIP() { + CIRCSock* pIRCSock = GetIRCSock(); + + if (pIRCSock) { + return pIRCSock->GetLocalIP(); + } + + CUserSock* pUserSock = GetUserSock(); + + if (pUserSock) { + return pUserSock->GetLocalIP(); + } + + return ""; +} + +bool CUser::PutIRC(const string& sLine) { + CIRCSock* pIRCSock = GetIRCSock(); + + if (!pIRCSock) { + return false; + } + + pIRCSock->PutServ(sLine); + return true; +} + +bool CUser::PutUser(const string& sLine) { + CUserSock* pUserSock = GetUserSock(); + + if (!pUserSock) { + return false; + } + + pUserSock->PutServ(sLine); + return true; +} + +bool CUser::PutStatus(const string& sLine) { + CUserSock* pUserSock = GetUserSock(); + + if (!pUserSock) { + return false; + } + + pUserSock->PutStatus(sLine); + return true; +} + +bool CUser::PutModule(const string& sModule, const string& sLine) { + CUserSock* pUserSock = GetUserSock(); + + if (!pUserSock) { + return false; + } + + pUserSock->PutModule(sModule, sLine); + return true; +} + +bool CUser::ResumeFile(const string& sRemoteNick, unsigned short uPort, unsigned long uFileSize) { + TSocketManager& Manager = m_pZNC->GetManager(); + + for (unsigned int a = 0; a < Manager.size(); a++) { + if (strncasecmp(Manager[a]->GetSockName().c_str(), "DCC::LISTEN::", 13) == 0) { + CDCCSock* pSock = (CDCCSock*) Manager[a]; + + if (pSock->GetLocalPort() == uPort) { + if (pSock->Seek(uFileSize)) { + PutModule(pSock->GetModuleName(), "DCC -> [" + pSock->GetRemoteNick() + "][" + pSock->GetFileName() + "] - Attempting to resume from file position [" + CUtils::ToString(uFileSize) + "]"); + return true; + } else { + return false; + } + } + } + } + + return false; +} + +bool CUser::SendFile(const string& sRemoteNick, const string& sFileName, const string& sModuleName) { + string sFullPath = CUtils::ChangeDir(GetDLPath(), sFileName, GetHomePath()); + CDCCSock* pSock = new CDCCSock(this, sRemoteNick, sFullPath, sModuleName); + + CFile* pFile = pSock->OpenFile(false); + + if (!pFile) { + delete pSock; + return false; + } + + int iPort = GetManager()->ListenAllRand("DCC::LISTEN::" + sRemoteNick, false, SOMAXCONN, pSock, 120); + + if (strcasecmp(GetNick().c_str(), sRemoteNick.c_str()) == 0) { + PutUser(":" + GetStatusPrefix() + "status!znc@znc.com PRIVMSG " + sRemoteNick + " :\001DCC SEND " + pFile->GetShortName() + " " + CUtils::ToString(CUtils::GetLongIP(GetLocalIP())) + " " + + CUtils::ToString(iPort) + " " + CUtils::ToString(pFile->GetSize()) + "\001"); + } else { + PutIRC("PRIVMSG " + sRemoteNick + " :\001DCC SEND " + pFile->GetShortName() + " " + CUtils::ToString(CUtils::GetLongIP(GetLocalIP())) + " " + + CUtils::ToString(iPort) + " " + CUtils::ToString(pFile->GetSize()) + "\001"); + } + + PutModule(sModuleName, "DCC -> [" + sRemoteNick + "][" + pFile->GetShortName() + "] - Attempting Send."); + return true; +} + +bool CUser::GetFile(const string& sRemoteNick, const string& sRemoteIP, unsigned short uRemotePort, const string& sFileName, unsigned long uFileSize, const string& sModuleName) { + if (CFile::Exists(sFileName)) { + PutModule(sModuleName, "DCC <- [" + sRemoteNick + "][" + sFileName + "] - File already exists."); + return false; + } + + CDCCSock* pSock = new CDCCSock(this, sRemoteNick, sRemoteIP, uRemotePort, sFileName, uFileSize, sModuleName); + + if (!pSock->OpenFile()) { + delete pSock; + return false; + } + + if (!GetManager()->Connect(sRemoteIP, uRemotePort, "DCC::GET::" + sRemoteNick, 60, false, GetLocalIP(), pSock)) { + PutModule(sModuleName, "DCC <- [" + sRemoteNick + "][" + sFileName + "] - Unable to connect."); + return false; + } + + PutModule(sModuleName, "DCC <- [" + sRemoteNick + "][" + sFileName + "] - Attempting to connect to [" + sRemoteIP + "]"); + return true; +} + +string CUser::GetCurNick() { + CIRCSock* pIRCSock = GetIRCSock(); + + if (pIRCSock) { + return pIRCSock->GetNick(); + } + + CUserSock* pUserSock = GetUserSock(); + + if (pUserSock) { + return pUserSock->GetNick(); + } + + return ""; +} + +// Setters +void CUser::SetNick(const string& s) { m_sNick = s; } +void CUser::SetAltNick(const string& s) { m_sAltNick = s; } +void CUser::SetIdent(const string& s) { m_sIdent = s; } +void CUser::SetRealName(const string& s) { m_sRealName = s; } +void CUser::SetVHost(const string& s) { m_sVHost = s; } +void CUser::SetPass(const string& s, bool bHashed) { m_sPass = s; m_bPassHashed = bHashed; } +void CUser::SetUseClientIP(bool b) { m_bUseClientIP = b; } +void CUser::SetKeepNick(bool b) { m_bKeepNick = b; } +void CUser::SetDenyLoadMod(bool b) { m_bDenyLoadMod = b; } +void CUser::SetDefaultChanModes(const string& s) { m_sDefaultChanModes = s; } + +bool CUser::SetStatusPrefix(const string& s) { + if ((!s.empty()) && (s.length() < 6) && (s.find(' ') == string::npos)) { + m_sStatusPrefix = s; + return true; + } + + return false; +} +// !Setters + +// Getters +const string& CUser::GetUserName() const { return m_sUserName; } +const string& CUser::GetNick() const { return m_sNick; } +const string& CUser::GetAltNick() const { return m_sAltNick; } +const string& CUser::GetIdent() const { return m_sIdent; } +const string& CUser::GetRealName() const { return m_sRealName; } +const string& CUser::GetVHost() const { return m_sVHost; } +const string& CUser::GetPass() const { return m_sPass; } + +const string& CUser::GetBinPath() const { return m_pZNC->GetBinPath(); } +const string& CUser::GetDLPath() const { return m_pZNC->GetDLPath(); } +const string& CUser::GetModPath() const { return m_pZNC->GetModPath(); } +const string& CUser::GetHomePath() const { return m_pZNC->GetHomePath(); } + +bool CUser::UseClientIP() const { return m_bUseClientIP; } +bool CUser::KeepNick() const { return m_bKeepNick; } +bool CUser::DenyLoadMod() const { return m_bDenyLoadMod; } +const string& CUser::GetStatusPrefix() const { return m_sStatusPrefix; } +const string& CUser::GetDefaultChanModes() const { return m_sDefaultChanModes; } +const vector& CUser::GetChans() const { return m_vChans; } +// !Getters diff --git a/User.h b/User.h new file mode 100644 index 00000000..b62a402c --- /dev/null +++ b/User.h @@ -0,0 +1,124 @@ +#ifndef _USER_H +#define _USER_H + +#ifdef _MODULES +#include "Modules.h" +#endif + +#include +#include + +using std::vector; +using std::set; + +class CZNC; +class CChan; +class CServer; +class CIRCSock; +class CUserSock; + +class CUser { +public: + CUser(const string& sUserName, CZNC* pZNC); + virtual ~CUser(); + + CChan* FindChan(const string& sName); + bool AddChan(CChan* pChan); + bool AddChan(const string& sName, unsigned int uBufferCount = 50); + bool DelChan(const string& sName); + bool AddServer(const string& sName); + bool AddServer(const string& sName, unsigned short uPort, const string& sPass = "", bool bSSL = false); + CServer* GetNextServer(); + bool CheckPass(const string& sPass); + bool AddAllowedHost(const string& sHostMask); + bool IsHostAllowed(const string& sHostMask); + +#ifdef _MODULES + // Modules + CModules& GetModules() { return m_Modules; } + // !Modules +#endif + + bool OnBoot(); + bool IsUserAttached(); + + bool PutIRC(const string& sLine); + bool PutUser(const string& sLine); + bool PutStatus(const string& sLine); + bool PutModule(const string& sModule, const string& sLine); + + string GetLocalIP(); + + bool SendFile(const string& sRemoteNick, const string& sFileName, const string& sModuleName = ""); + bool GetFile(const string& sRemoteNick, const string& sRemoteIP, unsigned short uRemotePort, const string& sFileName, unsigned long uFileSize, const string& sModuleName = ""); + bool ResumeFile(const string& sRemoteNick, unsigned short uPort, unsigned long uFileSize); + string GetCurNick(); + + // Setters + void SetNick(const string& s); + void SetAltNick(const string& s); + void SetIdent(const string& s); + void SetRealName(const string& s); + void SetVHost(const string& s); + void SetPass(const string& s, bool bHashed); + void SetUseClientIP(bool b); + void SetKeepNick(bool b); + void SetDenyLoadMod(bool b); + bool SetStatusPrefix(const string& s); + void SetDefaultChanModes(const string& s); + // !Setters + + // Getters + CUserSock* GetUserSock(); + CIRCSock* GetIRCSock(); + TSocketManager* GetManager(); + CZNC* GetZNC(); + const string& GetUserName() const; + const string& GetNick() const; + const string& GetAltNick() const; + const string& GetIdent() const; + const string& GetRealName() const; + const string& GetVHost() const; + const string& GetPass() const; + + const string& GetBinPath() const; + const string& GetDLPath() const; + const string& GetModPath() const; + const string& GetHomePath() const; + + bool UseClientIP() const; + bool KeepNick() const; + bool DenyLoadMod() const; + const string& GetStatusPrefix() const; + const string& GetDefaultChanModes() const; + const vector& GetChans() const; + // !Getters +private: +protected: + CZNC* m_pZNC; + + string m_sUserName; + string m_sNick; + string m_sAltNick; + string m_sIdent; + string m_sRealName; + string m_sVHost; + string m_sPass; + string m_sStatusPrefix; + string m_sDefaultChanModes; + + bool m_bPassHashed; + bool m_bUseClientIP; + bool m_bKeepNick; + bool m_bDenyLoadMod; + vector m_vServers; + vector m_vChans; + set m_ssAllowedHosts; + unsigned int m_uServerIdx; + +#ifdef _MODULES + CModules m_Modules; +#endif +}; + +#endif // !_USER_H diff --git a/UserSock.cpp b/UserSock.cpp new file mode 100644 index 00000000..a56b528b --- /dev/null +++ b/UserSock.cpp @@ -0,0 +1,846 @@ +#include "main.h" +#include "UserSock.h" +#include "User.h" +#include "znc.h" +#include "IRCSock.h" +#include "DCCBounce.h" +#include "DCCSock.h" + +void CUserSock::ReadLine(const string& sData) { + string sLine = sData; + + while ((CUtils::Right(sLine, 1) == "\r") || (CUtils::Right(sLine, 1) == "\n")) { + CUtils::RightChomp(sLine); + } + + DEBUG_ONLY(cout << GetSockName() << " <- [" << sLine << "]" << endl); + +#ifdef _MODULES + if (m_bAuthed) { + if ((m_pUser) && (m_pUser->GetModules().OnUserRaw(sLine))) { + return; + } + } +#endif + + string sCommand = CUtils::Token(sLine, 0); + + if (strcasecmp(sCommand.c_str(), "ZNC") == 0) { + PutStatus("Hello. How may I help you?"); + return; + } else if (strcasecmp(sCommand.c_str(), "PONG") == 0) { + return; // Block pong replies, we already responded to the pings + } else if (strcasecmp(sCommand.c_str(), "PASS") == 0) { + m_sPass = CUtils::Token(sLine, 1); + + if (m_sPass.find(":") != string::npos) { + m_sUser = CUtils::Token(m_sPass, 0, false, ':'); + m_sPass = CUtils::Token(m_sPass, 1, true, ':'); + } + + m_bGotUser = (!m_sUser.empty()); + + if ((m_bGotNick) && (m_bGotUser)) { + AuthUser(); + } + + return; // Don't forward this msg. ZNC has already registered us. + } else if (strcasecmp(sCommand.c_str(), "NICK") == 0) { + string sNick = CUtils::Token(sLine, 1); + if (CUtils::Left(sNick, 1) == ":") { + CUtils::LeftChomp(sNick); + } + + if (!m_bAuthed) { + m_sNick = sNick; + m_bGotNick = true; + + if (m_bGotUser) { + AuthUser(); + } + return; // Don't forward this msg. The bnc will handle nick changes until auth is complete + } + + if ((m_pUser) && (strcasecmp(sNick.c_str(), m_pUser->GetNick().c_str()) == 0)) { + m_uKeepNickCounter++; + } + } else if (strcasecmp(sCommand.c_str(), "USER") == 0) { + if ((!m_bGotUser) && (!m_bAuthed)) { + m_sUser = CUtils::Token(sLine, 1); + m_bGotUser = true; + + if (m_bGotNick) { + AuthUser(); + } + } + + return; // Don't forward this msg. ZNC has already registered us. + } else if (strcasecmp(sCommand.c_str(), "JOIN") == 0) { + string sChan = CUtils::Token(sLine, 1); + if (CUtils::Left(sChan, 1) == ":") { + CUtils::LeftChomp(sChan); + } + + if (m_pUser) { + CChan* pChan = m_pUser->FindChan(sChan); + + if ((pChan) && (!pChan->IsOn())) { + pChan->IncClientRequests(); + } + } + } else if (strcasecmp(sCommand.c_str(), "QUIT") == 0) { + if (m_pIRCSock) { + m_pIRCSock->UserDisconnected(); + } + + Close(); // Treat a client quit as a detach + return; // Don't forward this msg. We don't want the client getting us disconnected. + } else if (strcasecmp(sCommand.c_str(), "NOTICE") == 0) { + string sTarget = CUtils::Token(sLine, 1); + string sMsg = CUtils::Token(sLine, 2, true); + + if (CUtils::Left(sMsg, 1) == ":") { + CUtils::LeftChomp(sMsg); + } + + if ((!m_pUser) || (strcasecmp(sTarget.c_str(), string(m_pUser->GetStatusPrefix() + "status").c_str())) == 0) { + return; + } + + if (strncasecmp(sTarget.c_str(), m_pUser->GetStatusPrefix().c_str(), m_pUser->GetStatusPrefix().length()) == 0) { +#ifdef _MODULES + if (m_pUser) { + string sModule = sTarget; + CUtils::LeftChomp(sModule, m_pUser->GetStatusPrefix().length()); + + CModule* pModule = m_pUser->GetModules().FindModule(sModule); + if (pModule) { + pModule->OnModNotice(sMsg); + } else { + PutStatus("No such module [" + sModule + "]"); + } + } +#endif + return; + } + + if (CUtils::WildCmp("DCC * (*)", sMsg.c_str())) { + sMsg = "DCC " + CUtils::Token(sLine, 3) + " (" + ((m_pIRCSock) ? m_pIRCSock->GetLocalIP() : GetLocalIP()) + ")"; + } + + CChan* pChan = m_pUser->FindChan(sTarget); + + if ((pChan) && (pChan->KeepBuffer())) { + pChan->AddBuffer(":" + GetNickMask() + " NOTICE " + sTarget + " :" + sMsg); + } + +#ifdef _MODULES + if (CUtils::WildCmp("\001*\001", sMsg.c_str())) { + string sCTCP = sMsg; + CUtils::LeftChomp(sCTCP); + CUtils::RightChomp(sCTCP); + + if ((m_pUser) && (m_pUser->GetModules().OnUserCTCPReply(sTarget, sCTCP))) { + return; + } + + sMsg = "\001" + sCTCP + "\001"; + } else { + if ((m_pUser) && (m_pUser->GetModules().OnUserNotice(sTarget, sMsg))) { + return; + } + } +#endif + + PutIRC("NOTICE " + sTarget + " :" + sMsg); + return; + } else if (strcasecmp(sCommand.c_str(), "PRIVMSG") == 0) { + string sTarget = CUtils::Token(sLine, 1); + string sMsg = CUtils::Token(sLine, 2, true); + + if (CUtils::Left(sMsg, 1) == ":") { + CUtils::LeftChomp(sMsg); + } + + if (CUtils::WildCmp("\001*\001", sMsg.c_str())) { + string sCTCP = sMsg; + CUtils::LeftChomp(sCTCP); + CUtils::RightChomp(sCTCP); + + if (strncasecmp(sCTCP.c_str(), "DCC ", 4) == 0) { + string sType = CUtils::Token(sCTCP, 1); + string sFile = CUtils::Token(sCTCP, 2); + unsigned long uLongIP = strtoul(CUtils::Token(sCTCP, 3).c_str(), NULL, 10); + unsigned short uPort = strtoul(CUtils::Token(sCTCP, 4).c_str(), NULL, 10); + unsigned long uFileSize = strtoul(CUtils::Token(sCTCP, 5).c_str(), NULL, 10); + string sIP = (m_pIRCSock) ? m_pIRCSock->GetLocalIP() : GetLocalIP(); + + if (!m_pUser->UseClientIP()) { + uLongIP = CUtils::GetLongIP(GetRemoteIP()); + } + + if (strcasecmp(sType.c_str(), "CHAT") == 0) { + if (strncasecmp(sTarget.c_str(), m_pUser->GetStatusPrefix().c_str(), m_pUser->GetStatusPrefix().length()) == 0) { + } else { + unsigned short uBNCPort = CDCCBounce::DCCRequest(sTarget, uLongIP, uPort, "", true, m_pUser, (m_pIRCSock) ? m_pIRCSock->GetLocalIP() : GetLocalIP(), ""); + if (uBNCPort) { + PutIRC("PRIVMSG " + sTarget + " :\001DCC CHAT chat " + CUtils::ToString(CUtils::GetLongIP(sIP)) + " " + CUtils::ToString(uBNCPort) + "\001"); + } + } + } else if (strcasecmp(sType.c_str(), "SEND") == 0) { + // DCC SEND readme.txt 403120438 5550 1104 + + if (strncasecmp(sTarget.c_str(), m_pUser->GetStatusPrefix().c_str(), m_pUser->GetStatusPrefix().length()) == 0) { + if ((!m_pUser) || (strcasecmp(sTarget.c_str(), string(m_pUser->GetStatusPrefix() + "status").c_str()) == 0)) { + if (!m_pUser) { + return; + } + + string sPath = m_pUser->GetDLPath(); + if (!CFile::Exists(sPath)) { + PutStatus("Directory [" + sPath + "] not found, creating."); + if (mkdir(sPath.c_str(), 0700)) { + PutStatus("Could not create [" + sPath + "] directory."); + return; + } + } else if (!CFile::IsDir(sPath)) { + PutStatus("Error: [" + sPath + "] is not a directory."); + return; + } + + string sLocalFile = sPath + "/" + sFile; + + if (m_pUser) { + m_pUser->GetFile(GetNick(), CUtils::GetIP(uLongIP), uPort, sLocalFile, uFileSize); + } + } else { +#ifdef _MODULES + if ((m_pUser) && (m_pUser->GetModules().OnDCCUserSend(sTarget, uLongIP, uPort, sFile, uFileSize))) { + return; + } +#endif + } + } else { + unsigned short uBNCPort = CDCCBounce::DCCRequest(sTarget, uLongIP, uPort, sFile, false, m_pUser, (m_pIRCSock) ? m_pIRCSock->GetLocalIP() : GetLocalIP(), ""); + if (uBNCPort) { + PutIRC("PRIVMSG " + sTarget + " :\001DCC SEND " + sFile + " " + CUtils::ToString(CUtils::GetLongIP(sIP)) + " " + CUtils::ToString(uBNCPort) + " " + CUtils::ToString(uFileSize) + "\001"); + } + } + } else if (strcasecmp(sType.c_str(), "RESUME") == 0) { + // PRIVMSG user :DCC RESUME "znc.o" 58810 151552 + unsigned short uResumePort = atoi(CUtils::Token(sCTCP, 3).c_str()); + unsigned long uResumeSize = strtoul(CUtils::Token(sCTCP, 4).c_str(), NULL, 10); + + // Need to lookup the connection by port, filter the port, and forward to the user + if (strncasecmp(sTarget.c_str(), m_pUser->GetStatusPrefix().c_str(), m_pUser->GetStatusPrefix().length()) == 0) { + if ((m_pUser) && (m_pUser->ResumeFile(sTarget, uResumePort, uResumeSize))) { + PutServ(":" + sTarget + "!znc@znc.com PRIVMSG " + GetNick() + " :\001DCC ACCEPT " + sFile + " " + CUtils::ToString(uResumePort) + " " + CUtils::ToString(uResumeSize) + "\001"); + } else { + PutStatus("DCC -> [" + GetNick() + "][" + sFile + "] Unable to find send to initiate resume."); + } + } else { + CDCCBounce* pSock = (CDCCBounce*) m_pZNC->GetManager().FindSockByLocalPort(uResumePort); + if ((pSock) && (strncasecmp(pSock->GetSockName().c_str(), "DCC::", 5) == 0)) { + PutIRC("PRIVMSG " + sTarget + " :\001DCC " + sType + " " + sFile + " " + CUtils::ToString(pSock->GetUserPort()) + " " + CUtils::Token(sCTCP, 4) + "\001"); + } + } + } else if (strcasecmp(sType.c_str(), "ACCEPT") == 0) { + if (strncasecmp(sTarget.c_str(), m_pUser->GetStatusPrefix().c_str(), m_pUser->GetStatusPrefix().length()) == 0) { + } else { + // Need to lookup the connection by port, filter the port, and forward to the user + TSocketManager& Manager = m_pZNC->GetManager(); + + for (unsigned int a = 0; a < Manager.size(); a++) { + CDCCBounce* pSock = (CDCCBounce*) Manager[a]; + + if ((pSock) && (strncasecmp(pSock->GetSockName().c_str(), "DCC::", 5) == 0)) { + if (pSock->GetUserPort() == atoi(CUtils::Token(sCTCP, 3).c_str())) { + PutIRC("PRIVMSG " + sTarget + " :\001DCC " + sType + " " + sFile + " " + CUtils::ToString(pSock->GetLocalPort()) + " " + CUtils::Token(sCTCP, 4) + "\001"); + } + } + } + } + } + + return; + } + + if (strncasecmp(sTarget.c_str(), m_pUser->GetStatusPrefix().c_str(), m_pUser->GetStatusPrefix().length()) == 0) { +#ifdef _MODULES + string sModule = sTarget; + CUtils::LeftChomp(sModule, m_pUser->GetStatusPrefix().length()); + + CModule* pModule = m_pUser->GetModules().FindModule(sModule); + if (pModule) { + pModule->OnModCTCP(sCTCP); + } else { + PutStatus("No such module [" + sModule + "]"); + } +#endif + return; + } + +#ifdef _MODULES + if ((m_pUser) && (m_pUser->GetModules().OnUserCTCP(sTarget, sCTCP))) { + return; + } +#endif + + PutIRC("PRIVMSG " + sTarget + " :\001" + sCTCP + "\001"); + return; + } + + if ((m_pUser) && (strcasecmp(sTarget.c_str(), string(m_pUser->GetStatusPrefix() + "status").c_str()) == 0)) { +#ifdef _MODULES + if ((m_pUser) && (m_pUser->GetModules().OnStatusCommand(sMsg))) { + return; + } +#endif + UserCommand(sMsg); + return; + } + + if (strncasecmp(sTarget.c_str(), m_pUser->GetStatusPrefix().c_str(), m_pUser->GetStatusPrefix().length()) == 0) { +#ifdef _MODULES + if (m_pUser) { + string sModule = sTarget; + CUtils::LeftChomp(sModule, m_pUser->GetStatusPrefix().length()); + + CModule* pModule = m_pUser->GetModules().FindModule(sModule); + if (pModule) { + pModule->OnModCommand(sMsg); + } else { + PutStatus("No such module [" + sModule + "]"); + } + } +#endif + return; + } + + CChan* pChan = m_pUser->FindChan(sTarget); + + if ((pChan) && (pChan->KeepBuffer())) { + pChan->AddBuffer(":" + GetNickMask() + " PRIVMSG " + sTarget + " :" + sMsg); + } + +#ifdef _MODULES + if ((m_pUser) && (m_pUser->GetModules().OnUserMsg(sTarget, sMsg))) { + return; + } +#endif + PutIRC("PRIVMSG " + sTarget + " :" + sMsg); + return; + } + + PutIRC(sLine); +} + +void CUserSock::SetNick(const string& s) { + m_sNick = s; + + if (m_pUser) { + if (strcasecmp(m_sNick.c_str(), m_pUser->GetNick().c_str()) == 0) { + m_uKeepNickCounter = 0; + } + } +} + +bool CUserSock::DecKeepNickCounter() { + if (!m_uKeepNickCounter) { + return false; + } + + m_uKeepNickCounter--; + return true; +} + +void CUserSock::UserCommand(const string& sLine) { + if (!m_pUser) { + return; + } + + if (sLine.empty()) { + return; + } + + string sCommand = CUtils::Token(sLine, 0); + + if (strcasecmp(sCommand.c_str(), "LISTNICKS") == 0) { + string sChan = CUtils::Token(sLine, 1); + + if (sChan.empty()) { + PutStatus("Usage: ListNicks <#chan>"); + return; + } + + CChan* pChan = m_pUser->FindChan(sChan); + + if (!pChan) { + PutStatus("You are not on [" + sChan + "]"); + return; + } + + if (!pChan->IsOn()) { + PutStatus("You are not on [" + sChan + "] [trying]"); + return; + } + + const map& msNicks = pChan->GetNicks(); + + if (!msNicks.size()) { + PutStatus("No nicks on [" + sChan + "]"); + return; + } + + CTable Table; + Table.AddColumn("@"); + Table.AddColumn("+"); + Table.AddColumn("Nick"); + Table.AddColumn("Ident"); + Table.AddColumn("Host"); + + for (map::const_iterator a = msNicks.begin(); a != msNicks.end(); a++) { + Table.AddRow(); + if (a->second->IsOp()) { Table.SetCell("@", "@"); } + if (a->second->IsVoice()) { Table.SetCell("+", "+"); } + Table.SetCell("Nick", a->second->GetNick()); + Table.SetCell("Ident", a->second->GetIdent()); + Table.SetCell("Host", a->second->GetHost()); + } + + unsigned int uTableIdx = 0; + string sLine; + + while (Table.GetLine(uTableIdx++, sLine)) { + PutStatus(sLine); + } + } else if (strcasecmp(sCommand.c_str(), "JUMP") == 0) { + if (m_pUser) { + if (m_pIRCSock) { + m_pIRCSock->Close(); + PutStatus("Jumping to the next server in the list..."); + return; + } + } + } else if (strcasecmp(sCommand.c_str(), "LISTCHANS") == 0) { + if (m_pUser) { + const vector& vChans = m_pUser->GetChans(); + + CTable Table; + Table.AddColumn("Name"); + Table.AddColumn("Status"); + Table.AddColumn("Buf"); + Table.AddColumn("Modes"); + Table.AddColumn("Users"); + Table.AddColumn("+o"); + Table.AddColumn("+v"); + + for (unsigned int a = 0; a < vChans.size(); a++) { + CChan* pChan = vChans[a]; + Table.AddRow(); + Table.SetCell("Name", pChan->GetName()); + Table.SetCell("Status", ((vChans[a]->IsOn()) ? "Joined" : "Trying")); + Table.SetCell("Buf", CUtils::ToString(pChan->GetBufferCount())); + + string sModes = pChan->GetModeString(); + unsigned int uLimit = pChan->GetLimit(); + const string& sKey = pChan->GetKey(); + + if (uLimit) { sModes += " " + CUtils::ToString(uLimit); } + if (!sKey.empty()) { sModes += " " + sKey; } + + Table.SetCell("Modes", sModes); + Table.SetCell("Users", CUtils::ToString(pChan->GetNickCount())); + Table.SetCell("+o", CUtils::ToString(pChan->GetOpCount())); + Table.SetCell("+v", CUtils::ToString(pChan->GetVoiceCount())); + } + + if (Table.size()) { + unsigned int uTableIdx = 0; + string sLine; + + while (Table.GetLine(uTableIdx++, sLine)) { + PutStatus(sLine); + } + } + } + } else if (strcasecmp(sCommand.c_str(), "SEND") == 0) { + string sToNick = CUtils::Token(sLine, 1); + string sFile = CUtils::Token(sLine, 2); + + if ((sToNick.empty()) || (sFile.empty())) { + PutStatus("Usage: Send "); + return; + } + + if (m_pUser) { + m_pUser->SendFile(sToNick, sFile); + } + } else if (strcasecmp(sCommand.c_str(), "GET") == 0) { + string sFile = CUtils::Token(sLine, 1); + + if (sFile.empty()) { + PutStatus("Usage: Get "); + return; + } + + if (m_pUser) { + m_pUser->SendFile(GetNick(), sFile); + } + } else if (strcasecmp(sCommand.c_str(), "LISTDCCS") == 0) { + TSocketManager& Manager = m_pZNC->GetManager(); + + CTable Table; + Table.AddColumn("Type"); + Table.AddColumn("State"); + Table.AddColumn("Speed"); + Table.AddColumn("Nick"); + Table.AddColumn("IP"); + Table.AddColumn("File"); + + for (unsigned int a = 0; a < Manager.size(); a++) { + const string& sSockName = Manager[a]->GetSockName(); + + if (strncasecmp(sSockName.c_str(), "DCC::", 5) == 0) { + if (strncasecmp(sSockName.c_str() +5, "XFER::REMOTE::", 14) == 0) { + continue; + } + + if (strncasecmp(sSockName.c_str() +5, "CHAT::REMOTE::", 14) == 0) { + continue; + } + + if (strncasecmp(sSockName.c_str() +5, "SEND", 4) == 0) { + CDCCSock* pSock = (CDCCSock*) Manager[a]; + + Table.AddRow(); + Table.SetCell("Type", "Sending"); + Table.SetCell("State", CUtils::ToPercent(pSock->GetProgress())); + Table.SetCell("Speed", CUtils::ToKBytes(pSock->GetAvgWrite() / 1000.0)); + Table.SetCell("Nick", pSock->GetRemoteNick()); + Table.SetCell("IP", pSock->GetRemoteIP()); + Table.SetCell("File", pSock->GetFileName()); + } else if (strncasecmp(sSockName.c_str() +5, "GET", 3) == 0) { + CDCCSock* pSock = (CDCCSock*) Manager[a]; + + Table.AddRow(); + Table.SetCell("Type", "Getting"); + Table.SetCell("State", CUtils::ToPercent(pSock->GetProgress())); + Table.SetCell("Speed", CUtils::ToKBytes(pSock->GetAvgRead() / 1000.0)); + Table.SetCell("Nick", pSock->GetRemoteNick()); + Table.SetCell("IP", pSock->GetRemoteIP()); + Table.SetCell("File", pSock->GetFileName()); + } else if (strncasecmp(sSockName.c_str() +5, "LISTEN", 6) == 0) { + CDCCSock* pSock = (CDCCSock*) Manager[a]; + + Table.AddRow(); + Table.SetCell("Type", "Sending"); + Table.SetCell("State", "Waiting"); + Table.SetCell("Nick", pSock->GetRemoteNick()); + Table.SetCell("IP", pSock->GetRemoteIP()); + Table.SetCell("File", pSock->GetFileName()); + } else if (strncasecmp(sSockName.c_str() +5, "XFER::LOCAL", 11) == 0) { + CDCCBounce* pSock = (CDCCBounce*) Manager[a]; + + string sState = "Waiting"; + if ((pSock->IsConnected()) || (pSock->IsPeerConnected())) { + sState = "Halfway"; + if ((pSock->IsPeerConnected()) && (pSock->IsPeerConnected())) { + sState = "Connected"; + } + } + + Table.AddRow(); + Table.SetCell("Type", "Xfer"); + Table.SetCell("State", sState); + Table.SetCell("Nick", pSock->GetRemoteNick()); + Table.SetCell("IP", pSock->GetRemoteIP()); + Table.SetCell("File", pSock->GetFileName()); + } else if (strncasecmp(sSockName.c_str() +5, "CHAT::LOCAL", 11) == 0) { + CDCCBounce* pSock = (CDCCBounce*) Manager[a]; + + string sState = "Waiting"; + if ((pSock->IsConnected()) || (pSock->IsPeerConnected())) { + sState = "Halfway"; + if ((pSock->IsPeerConnected()) && (pSock->IsPeerConnected())) { + sState = "Connected"; + } + } + + Table.AddRow(); + Table.SetCell("Type", "Chat"); + Table.SetCell("State", sState); + Table.SetCell("Nick", pSock->GetRemoteNick()); + Table.SetCell("IP", pSock->GetRemoteIP()); + } + } + } + + if (Table.size()) { + unsigned int uTableIdx = 0; + string sLine; + + while (Table.GetLine(uTableIdx++, sLine)) { + PutStatus(sLine); + } + } else { + PutStatus("You have no active DCCs."); + } + } else if ((strcasecmp(sCommand.c_str(), "LISTMODS") == 0) || (strcasecmp(sCommand.c_str(), "LISTMODULES") == 0)) { +#ifdef _MODULES + if (m_pUser) { + CModules& Modules = m_pUser->GetModules(); + + if (!Modules.size()) { + PutStatus("You have no modules loaded."); + return; + } + + CTable Table; + Table.AddColumn("Name"); + Table.AddColumn("Description"); + + for (unsigned int b = 0; b < Modules.size(); b++) { + Table.AddRow(); + Table.SetCell("Name", Modules[b]->GetModName()); + Table.SetCell("Description", CUtils::Ellipsize(Modules[b]->GetDescription(), 128)); + } + + unsigned int uTableIdx = 0; string sLine; + while (Table.GetLine(uTableIdx++, sLine)) { + PutStatus(sLine); + } + } +#else + PutStatus("Modules are not enabled."); +#endif + return; + } else if ((strcasecmp(sCommand.c_str(), "LOADMOD") == 0) || (strcasecmp(sCommand.c_str(), "LOADMODULE") == 0)) { + string sMod = CUtils::Token(sLine, 1); + string sArgs = CUtils::Token(sLine, 2, true); + + if (m_pUser->DenyLoadMod()) { + PutStatus("Unable to load [" + sMod + "] Access Denied."); + return; + } +#ifdef _MODULES + if (sMod.empty()) { + PutStatus("Usage: LoadMod [args]"); + return; + } + + string sModRet; + m_pUser->GetModules().LoadModule(sMod, sArgs, m_pUser, m_pZNC->GetBinPath(), sModRet); + PutStatus(sModRet); +#else + PutStatus("Unable to load [" + sMod + "] Modules are not enabled."); +#endif + return; + } else if ((strcasecmp(sCommand.c_str(), "UNLOADMOD") == 0) || (strcasecmp(sCommand.c_str(), "UNLOADMODULE") == 0)) { + string sMod = CUtils::Token(sLine, 1); + + if (m_pUser->DenyLoadMod()) { + PutStatus("Unable to unload [" + sMod + "] Access Denied."); + return; + } +#ifdef _MODULES + if (sMod.empty()) { + PutStatus("Usage: UnloadMod "); + return; + } + + string sModRet; + m_pUser->GetModules().UnloadModule(sMod, sModRet); + PutStatus(sModRet); +#else + PutStatus("Unable to unload [" + sMod + "] Modules are not enabled."); +#endif + return; + } else if ((strcasecmp(sCommand.c_str(), "RELOADMOD") == 0) || (strcasecmp(sCommand.c_str(), "RELOADMODULE") == 0)) { + string sMod = CUtils::Token(sLine, 1); + string sArgs = CUtils::Token(sLine, 2, true); + + if (m_pUser->DenyLoadMod()) { + PutStatus("Unable to reload [" + sMod + "] Access Denied."); + return; + } +#ifdef _MODULES + if (sMod.empty()) { + PutStatus("Usage: ReloadMod [args]"); + return; + } + + string sModRet; + m_pUser->GetModules().ReloadModule(sMod, sArgs, m_pUser, m_pZNC->GetBinPath(), sModRet); + PutStatus(sModRet); +#else + PutStatus("Unable to unload [" + sMod + "] Modules are not enabled."); +#endif + return; + } else if (strcasecmp(sCommand.c_str(), "SETBUFFER") == 0) { + string sChan = CUtils::Token(sLine, 1); + + if (sChan.empty()) { + PutStatus("Usage: SetBuffer <#chan> [linecount]"); + return; + } + + CChan* pChan = m_pUser->FindChan(sChan); + + if (!pChan) { + PutStatus("You are not on [" + sChan + "]"); + return; + } + + if (!pChan->IsOn()) { + PutStatus("You are not on [" + sChan + "] [trying]"); + return; + } + + unsigned int uLineCount = strtoul(CUtils::Token(sLine, 2).c_str(), NULL, 10); + + if (uLineCount > 500) { + PutStatus("Max linecount is 500."); + return; + } + + pChan->SetBufferCount(uLineCount); + + PutStatus("BufferCount for [" + sChan + "] set to [" + CUtils::ToString(pChan->GetBufferCount()) + "]"); + } else { + PutStatus("I'm too stupid to help you with [" + sCommand + "]"); + } +} + +bool CUserSock::ConnectionFrom(const string& sHost, int iPort) { + DEBUG_ONLY(cout << GetSockName() << " == ConnectionFrom(" << sHost << ", " << iPort << ")" << endl); + return m_pZNC->IsHostAllowed(sHost); +} + +void CUserSock::AuthUser() { + if (!m_pZNC) { + DEBUG_ONLY(cout << "znc not set!" << endl); + Close(); + return; + } + + CUser* pUser = m_pZNC->GetUser(m_sUser); + + if ((!pUser) || (!pUser->CheckPass(m_sPass))) { + if (pUser) { + pUser->PutStatus("Another user attempted to login as you, with a bad password."); + } + + PutStatus("Bad username and/or password."); + PutServ(":irc.znc.com 464 " + GetNick() + " :Password Incorrect"); + Close(); + } else { + m_sPass = ""; + m_pUser = pUser; + + if (!m_pUser->IsHostAllowed(GetRemoteIP())) { + Close(); + return; + } + + m_bAuthed = true; + SetSockName("USR::" + pUser->GetUserName()); + + CIRCSock* pIRCSock = (CIRCSock*) m_pZNC->FindSockByName("IRC::" + pUser->GetUserName()); + + if (pIRCSock) { + m_pIRCSock = pIRCSock; + pIRCSock->UserConnected(this); + } + +#ifdef _MODULES + m_pUser->GetModules().OnUserAttached(); +#endif + } +} + +void CUserSock::Connected() { + DEBUG_ONLY(cout << GetSockName() << " == Connected();" << endl); + SetTimeout(900); // Now that we are connected, let nature take its course +} + +void CUserSock::ConnectionRefused() { + DEBUG_ONLY(cout << GetSockName() << " == ConnectionRefused()" << endl); +} + +void CUserSock::Disconnected() { + if (m_pIRCSock) { + m_pIRCSock->UserDisconnected(); + m_pIRCSock = NULL; + } + +#ifdef _MODULES + if (m_pUser) { + m_pUser->GetModules().OnUserDetached(); + } +#endif +} + +void CUserSock::IRCConnected(CIRCSock* pIRCSock) { + m_pIRCSock = pIRCSock; +} + +void CUserSock::BouncedOff() { + PutStatus("You are being disconnected because another user just authenticated as you."); + m_pIRCSock = NULL; + Close(); +} + +void CUserSock::IRCDisconnected() { + m_pIRCSock = NULL; +} + +Csock* CUserSock::GetSockObj(const string& sHost, int iPort) { + CUserSock* pSock = new CUserSock(sHost, iPort); + pSock->SetZNC(m_pZNC); + + return pSock; +} + +void CUserSock::PutIRC(const string& sLine) { + if (m_pIRCSock) { + m_pIRCSock->PutServ(sLine); + } +} + +void CUserSock::PutServ(const string& sLine) { + DEBUG_ONLY(cout << GetSockName() << " -> [" << sLine << "]" << endl); + Write(sLine + "\r\n"); +} + +void CUserSock::PutStatus(const string& sLine) { + PutModule("status", sLine); +} + +void CUserSock::PutModule(const string& sModule, const string& sLine) { + if (!m_pUser) { + return; + } + + DEBUG_ONLY(cout << GetSockName() << " -> [:" + m_pUser->GetStatusPrefix() + ((sModule.empty()) ? "status" : sModule) + "!znc@znc.com PRIVMSG " << GetNick() << " :" << sLine << "]" << endl); + Write(":" + m_pUser->GetStatusPrefix() + ((sModule.empty()) ? "status" : sModule) + "!znc@znc.com PRIVMSG " + GetNick() + " :" + sLine + "\r\n"); +} + +string CUserSock::GetNick() const { + string sRet; + + if ((m_bAuthed) && (m_pIRCSock)) { + sRet = m_pIRCSock->GetNick(); + } + + return (sRet.empty()) ? m_sNick : sRet; +} + +string CUserSock::GetNickMask() const { + if (m_pIRCSock) { + return m_pIRCSock->GetNickMask(); + } + + return GetNick() + "!" + m_pUser->GetIdent() + "@" + m_pUser->GetVHost(); +} + diff --git a/UserSock.h b/UserSock.h new file mode 100644 index 00000000..5889398f --- /dev/null +++ b/UserSock.h @@ -0,0 +1,73 @@ +#ifndef _USERSOCK_H +#define _USERSOCK_H + +#include "main.h" + +// Forward Declarations +class CZNC; +class CUser; +class CIRCSock; +// !Forward Declarations + + +class CUserSock : public Csock { +public: + CUserSock() : Csock() { + Init(); + } + CUserSock(const string& sHostname, int iport, int itimeout = 60) : Csock(sHostname, iport, itimeout) { + Init(); + } + virtual ~CUserSock() {} + + void Init() { + m_pZNC = NULL; + m_pUser = NULL; + m_pIRCSock = NULL; + m_bAuthed = false; + m_bGotNick = false; + m_bGotUser = false; + m_uKeepNickCounter = 0; + EnableReadLine(); + } + + string GetNick() const; + string GetNickMask() const; + + bool DecKeepNickCounter(); + void UserCommand(const string& sCommand); + void IRCConnected(CIRCSock* pIRCSock); + void IRCDisconnected(); + void BouncedOff(); + bool IsAttached() const { return m_bAuthed; } + + void PutIRC(const string& sLine); + void PutServ(const string& sLine); + void PutStatus(const string& sLine); + void PutModule(const string& sModule, const string& sLine); + + virtual void ReadLine(const string& sData); + void AuthUser(); + virtual void Connected(); + virtual void Disconnected(); + virtual void ConnectionRefused(); + virtual bool ConnectionFrom(const string& sHost, int iPort); + virtual Csock* GetSockObj(const string& sHost, int iPort); + + void SetZNC(CZNC* pZNC) { m_pZNC = pZNC; } + void SetNick(const string& s); +private: +protected: + bool m_bAuthed; + bool m_bGotNick; + bool m_bGotUser; + CZNC* m_pZNC; + CUser* m_pUser; + string m_sNick; + string m_sPass; + string m_sUser; + CIRCSock* m_pIRCSock; + unsigned int m_uKeepNickCounter; +}; + +#endif // !_USERSOCK_H diff --git a/Utils.cpp b/Utils.cpp new file mode 100644 index 00000000..8e60c431 --- /dev/null +++ b/Utils.cpp @@ -0,0 +1,687 @@ +#include "Utils.h" +#include +#include +#include +#include +#include +#include +#include +using std::stringstream; + +CUtils::CUtils() {} +CUtils::~CUtils() {} + +string CUtils::GetIP(unsigned long addr) { + char szBuf[16]; + memset((char*) szBuf, 0, 16); + + if (addr >= (1 << 24)) { + unsigned long ip[4]; + ip[0] = addr >> 24 & 255; + ip[1] = addr >> 16 & 255; + ip[2] = addr >> 8 & 255; + ip[3] = addr & 255; + sprintf(szBuf, "%lu.%lu.%lu.%lu", ip[0], ip[1], ip[2], ip[3]); + } + + return szBuf; +} + +unsigned long CUtils::GetLongIP(const string& sIP) { + register int i; + char *addr = (char *) malloc(sIP.length() +1); + char ip[4][4], n; + + strcpy(addr, sIP.c_str()); + + for (i=0; i<4; ip[0][i]=ip[1][i]=ip[2][i]=ip[3][i]='\0', i++); + + if (sscanf(addr, "%3[0-9].%3[0-9].%3[0-9].%3[0-9]%[^\n]", ip[0], ip[1], ip[2], ip[3], &n) != 4) { + free(addr); + return 0; + } + + free(addr); + return (unsigned long) ((atoi(ip[0]) << 24) + (atoi(ip[1]) << 16) + (atoi(ip[2]) << 8) + atoi(ip[3])); +} + +string CUtils::ChangeDir(const string& sPath, const string& sAdd, const string& sHomeDir) { + if (sAdd == "~") { + return sHomeDir; + } + + string sAddDir = sAdd; + + if (CUtils::Left(sAddDir, 2) == "~/") { + CUtils::LeftChomp(sAddDir); + sAddDir = sHomeDir + sAddDir; + } + + string sRet = ((sAddDir.size()) && (sAddDir[0] == '/')) ? "" : sPath; + sAddDir += "/"; + string sCurDir; + + if (CUtils::Right(sRet, 1) == "/") { + CUtils::RightChomp(sRet); + } + + for (unsigned int a = 0; a < sAddDir.size(); a++) { + switch (sAddDir[a]) { + case '/': + if (sCurDir == "..") { + sRet = sRet.substr(0, sRet.rfind('/')); + } else if ((sCurDir != "") && (sCurDir != ".")) { + sRet += "/" + sCurDir; + } + + sCurDir = ""; + break; + default: + sCurDir += sAddDir[a]; + break; + } + } + + return (sRet.empty()) ? "/" : sRet; +} + +string CUtils::ToString(short i) { stringstream s; s << i; return s.str(); } +string CUtils::ToString(unsigned short i) { stringstream s; s << i; return s.str(); } +string CUtils::ToString(int i) { stringstream s; s << i; return s.str(); } +string CUtils::ToString(unsigned int i) { stringstream s; s << i; return s.str(); } +string CUtils::ToString(long i) { stringstream s; s << i; return s.str(); } +string CUtils::ToString(unsigned long i) { stringstream s; s << i; return s.str(); } +string CUtils::ToString(unsigned long long i) { stringstream s; s << i; return s.str(); } +string CUtils::ToString(double i) { stringstream s; s << i; return s.str(); } +string CUtils::ToString(float i) { stringstream s; s << i; return s.str(); } + +string CUtils::ToPercent(double d) { + char szRet[32]; + snprintf(szRet, 32, "%.02f%%", d); + return szRet; +} + +string CUtils::ToKBytes(double d) { + char szRet[32]; + snprintf(szRet, 32, "%.0f K/s", d); + return szRet; +} + +string CUtils::Left(const string& s, unsigned int u) { + u = (u > s.length()) ? s.length() : u; + return s.substr(0, u); +} + +string CUtils::Right(const string& s, unsigned int u) { + u = (u > s.length()) ? s.length() : u; + return s.substr(s.length() - u, u); +} + +string& CUtils::Trim(string& s) { + while ((Right(s, 1) == " ") || (Right(s, 1) == "\t") || (Right(s, 1) == "\r") || (Right(s, 1) == "\n")) { + RightChomp(s); + } + + while ((Left(s, 1) == " ") || (Left(s, 1) == "\t") || (Left(s, 1) == "\r") || (Left(s, 1) == "\n")) { + LeftChomp(s); + } + + return s; +} + +string& CUtils::LeftChomp(string& s, unsigned int uLen) { + while ((uLen--) && (s.length())) { + s.erase(0, 1); + } + + return s; +} + +string& CUtils::RightChomp(string& s, unsigned int uLen) { + while ((uLen--) && (s.length())) { + s.erase(s.length() -1); + } + + return s; +} + +string CUtils::Token(const string& s, unsigned int uPos, bool bRest, char cSep) { + string sRet; + const char* p = s.c_str(); + + while (*p) { + if (uPos) { + if (*p == cSep) { + uPos--; + } + } else { + if ((*p == cSep) && (!bRest)) { + return sRet; + } + + sRet += *p; + } + + p++; + } + + return sRet; +} + +string CUtils::Ellipsize(const string& s, unsigned int uLen) { + if (uLen >= s.size()) { + return s; + } + + string sRet; + + if (uLen < 4) { + for (unsigned int a = 0; a < uLen; a++) { + sRet += "."; + } + + return sRet; + } + + sRet = s.substr(0, uLen -3) + "..."; + + return sRet; +} + +bool CUtils::WildCmp(const string& sWild, const string& sString) { + char *wild = (char*) sWild.c_str(); + char *string = (char*) sString.c_str(); + + register int i, len; + char *copy, *cp, *p, *lastmatch = string; + + if ((p = strchr (wild, '*')) == NULL) { + return (strcasecmp(wild, string) ? false : true); + } + + if (wild[0] != '*' && strncasecmp(string, wild, p-wild)) { + return false; + } + + len = strlen(wild) + 1; + + if (wild[len -2] != '*') { + p = strrchr(wild, '*') + 1; + if (strcasecmp(string + strlen(string) - strlen(p), p) != 0) { + return false; + } + } + + cp = copy = (char*) malloc(len); + memset((char*) copy, 0, len); + + for (i=0; i); + return size() -1; +} + +bool CTable::SetCell(const string& sColumn, const string& sValue, unsigned int uRowIdx) { + if (uRowIdx == (unsigned int) ~0) { + if (!size()) { + return false; + } + + uRowIdx = size() -1; + } + + (*(*this)[uRowIdx])[sColumn] = sValue; + return true; +} + +bool CTable::GetLine(unsigned int uIdx, string& sLine) { + stringstream ssRet; + + if (!size()) { + return false; + } + + if (uIdx == 1) { + m_msuWidths.clear(); // Clear out the width cache + ssRet.fill(' '); + ssRet << "| "; + + for (unsigned int a = 0; a < m_vsHeaders.size(); a++) { + ssRet.width(GetColumnWidth(a)); + ssRet << std::left << m_vsHeaders[a]; + ssRet << ((a == m_vsHeaders.size() -1) ? " |" : " | "); + } + + sLine = ssRet.str(); + return true; + } else if ((uIdx == 0) || (uIdx == 2) || (uIdx == (size() +3))) { + ssRet.fill('-'); + ssRet << "+-"; + + for (unsigned int a = 0; a < m_vsHeaders.size(); a++) { + ssRet.width(GetColumnWidth(a)); + ssRet << std::left << "-"; + ssRet << ((a == m_vsHeaders.size() -1) ? "-+" : "-+-"); + } + + sLine = ssRet.str(); + return true; + } else { + uIdx -= 3; + + if (uIdx < size()) { + map* pRow = (*this)[uIdx]; + ssRet.fill(' '); + ssRet << "| "; + + for (unsigned int c = 0; c < m_vsHeaders.size(); c++) { + ssRet.width(GetColumnWidth(c)); + ssRet << std::left << (*pRow)[m_vsHeaders[c]]; + ssRet << ((c == m_vsHeaders.size() -1) ? " |" : " | "); + } + + sLine = ssRet.str(); + return true; + } + } + + return false; +} + +/* +bool CTable::Output(std::ostream oOut) { + stringstream ssSep; + + ssSep << "-+-"; + + oOut << endl << ssSep.str() << endl; + + for (unsigned int b = 0; b < size(); b++) { + map* pRow = (*this)[b]; + + oOut << " | "; + + for (unsigned int c = 0; c < m_vsHeaders.size(); c++) { + oOut.width(GetColumnWidth(c)); + oOut << (*pRow)[m_vsHeaders[c]]; + oOut << " | "; + } + + oOut << endl; + } + + oOut << ssSep.str() << endl; + return true; +} +*/ + +unsigned int CTable::GetColumnWidth(unsigned int uIdx) { + if (uIdx >= m_vsHeaders.size()) { + return 0; + } + + const string& sColName = m_vsHeaders[uIdx]; + unsigned int uRet = sColName.size(); + map::iterator it = m_msuWidths.find(sColName); + + if (it != m_msuWidths.end()) { + return it->second; + } + + for (unsigned int a = 0; a < size(); a++) { + map* pRow = (*this)[a]; + uRet = uRet >? (*pRow)[m_vsHeaders[uIdx]].size(); + } + + return uRet; +} + + +CFile::CFile(const string& sLongName) { + m_sLongName = sLongName; + m_iFD = -1; + + m_sShortName = sLongName; + + while (CUtils::Left(m_sShortName, 1) == "/") { + CUtils::LeftChomp(m_sShortName); + } + + unsigned int uPos = m_sShortName.rfind('/'); + if (uPos != string::npos) { + m_sShortName = m_sShortName.substr(uPos +1); + } +} +CFile::~CFile() { + if (m_iFD != -1) { + Close(); + } +} + +bool CFile::IsReg(const string& sLongName, bool bUseLstat) { return CFile::FType(sLongName, FT_REGULAR, bUseLstat); } +bool CFile::IsDir(const string& sLongName, bool bUseLstat) { return CFile::FType(sLongName, FT_DIRECTORY, bUseLstat); } +bool CFile::IsChr(const string& sLongName, bool bUseLstat) { return CFile::FType(sLongName, FT_CHARACTER, bUseLstat); } +bool CFile::IsBlk(const string& sLongName, bool bUseLstat) { return CFile::FType(sLongName, FT_BLOCK, bUseLstat); } +bool CFile::IsFifo(const string& sLongName, bool bUseLstat) { return CFile::FType(sLongName, FT_FIFO, bUseLstat); } +bool CFile::IsLnk(const string& sLongName, bool bUseLstat) { return CFile::FType(sLongName, FT_LINK, bUseLstat); } +bool CFile::IsSock(const string& sLongName, bool bUseLstat) { return CFile::FType(sLongName, FT_SOCK, bUseLstat); } + +bool CFile::IsReg(bool bUseLstat) { return CFile::IsReg(m_sLongName, bUseLstat); } +bool CFile::IsDir(bool bUseLstat) { return CFile::IsDir(m_sLongName, bUseLstat); } +bool CFile::IsChr(bool bUseLstat) { return CFile::IsChr(m_sLongName, bUseLstat); } +bool CFile::IsBlk(bool bUseLstat) { return CFile::IsBlk(m_sLongName, bUseLstat); } +bool CFile::IsFifo(bool bUseLstat) { return CFile::IsFifo(m_sLongName, bUseLstat); } +bool CFile::IsLnk(bool bUseLstat) { return CFile::IsLnk(m_sLongName, bUseLstat); } +bool CFile::IsSock(bool bUseLstat) { return CFile::IsSock(m_sLongName, bUseLstat); } + +bool CFile::access(int mode) { return (::access(m_sLongName.c_str(), mode) == 0); } + +// for gettin file types, using fstat instead +bool CFile::FType(const string sFileName, EFileTypes eType, bool bUseLstat) { + struct stat st; + + if (!bUseLstat) { + if (stat(sFileName.c_str(), &st) != 0) { + return false; + } + } else { + if (lstat(sFileName.c_str(), &st) != 0) { + return false; + } + } + + switch (eType) { + case FT_REGULAR: + return S_ISREG(st.st_mode); + case FT_DIRECTORY: + return S_ISDIR(st.st_mode); + case FT_CHARACTER: + return S_ISCHR(st.st_mode); + case FT_BLOCK: + return S_ISBLK(st.st_mode); + case FT_FIFO: + return S_ISFIFO(st.st_mode); + case FT_LINK: + return S_ISLNK(st.st_mode); + case FT_SOCK: + return S_ISSOCK(st.st_mode); + default: + return false; + } + return false; +} + +// +// Functions to retrieve file information +// +bool CFile::Exists() const { return CFile::Exists(m_sLongName); } +unsigned long long CFile::GetSize() const { return CFile::GetSize(m_sLongName); } +unsigned int CFile::GetATime() const { return CFile::GetATime(m_sLongName); } +unsigned int CFile::GetMTime() const { return CFile::GetMTime(m_sLongName); } +unsigned int CFile::GetCTime() const { return CFile::GetCTime(m_sLongName); } +int CFile::GetUID() const { return CFile::GetUID(m_sLongName); } +int CFile::GetGID() const { return CFile::GetGID(m_sLongName); } +bool CFile::Exists(const string& sFile) { + struct stat st; + return (stat(sFile.c_str(), &st) == 0); +} + +unsigned long long CFile::GetSize(const string& sFile) { + struct stat st; + if(stat(sFile.c_str(), &st) != 0) { + return 0; + } + + return (S_ISREG(st.st_mode)) ? st.st_size : 0; +} + +unsigned int CFile::GetATime(const string& sFile) { + struct stat st; + return (stat(sFile.c_str(), &st) != 0) ? 0 : st.st_atime; +} + +unsigned int CFile::GetMTime(const string& sFile) { + struct stat st; + return (stat(sFile.c_str(), &st) != 0) ? 0 : st.st_mtime; +} + +unsigned int CFile::GetCTime(const string& sFile) { + struct stat st; + return (stat(sFile.c_str(), &st) != 0) ? 0 : st.st_ctime; +} + +int CFile::GetUID(const string& sFile) { + struct stat st; + return (stat(sFile.c_str(), &st) != 0) ? -1 : (int) st.st_uid; +} + +int CFile::GetGID(const string& sFile) { + struct stat st; + return (stat(sFile.c_str(), &st) != 0) ? -1 : (int) st.st_gid; +} +int CFile::GetInfo(const string& sFile, struct stat& st) { + return stat(sFile.c_str(), &st); +} + +// +// Functions to manipulate the file on the filesystem +// +int CFile::Delete() { return CFile::Delete(m_sLongName); } +int CFile::Move(const string& sNewFileName, bool bOverwrite) { + return CFile::Move(m_sLongName, sNewFileName, bOverwrite); +} + +bool CFile::Delete(const string& sFileName) { + if(!CFile::Exists(sFileName)) { + return false; + } + + return (unlink(sFileName.c_str()) == 0) ? true : false; +} + +bool CFile::Move(const string& sOldFileName, const string& sNewFileName, bool bOverwrite) { + if((!bOverwrite) && (CFile::Exists(sNewFileName))) { + return false; + } + + //string sNewLongName = (sNewFileName[0] == '/') ? sNewFileName : m_sPath + "/" + sNewFileName; + return (rename(sOldFileName.c_str(), sNewFileName.c_str()) == 0) ? true : false; +} + +bool CFile::Chmod(mode_t mode) { + return CFile::Chmod(m_sLongName, mode); +} + +bool CFile::Chmod(const string& sFile, mode_t mode) { + return (chmod(sFile.c_str(), mode) == 0); +} + +bool CFile::Seek(unsigned long uPos) { + return (m_iFD == -1) ? false : ((unsigned int) lseek(m_iFD, uPos, SEEK_SET) == uPos); +} + +bool CFile::Open(int iFlags, mode_t iMode) { + if (m_iFD != -1) { + return false; + } + + m_iFD = open(m_sLongName.c_str(), iFlags, iMode); + return (m_iFD > -1); +} + +int CFile::Read(char *pszBuffer, int iBytes) { + if (m_iFD == -1) { + return -1; + } + + return read(m_iFD, pszBuffer, iBytes); +} + +bool CFile::ReadLine(string & sData) { + char buff[64]; + sData.clear(); + if (m_iFD == -1) { + return false; + } + + bool bEOF = false; + + while(true) { + u_int iFind = m_sBuffer.find("\n"); + if (iFind != string::npos) { + sData = m_sBuffer.substr(0, (iFind + 1)); + m_sBuffer.erase(0, (iFind + 1)); + break; + } + + memset((char *)buff, '\0', 64); + int iBytes = read(m_iFD, buff, 64); + switch(iBytes) { + case -1: { + bEOF = true; + break; + } + case 0: { + bEOF = true; + break; + } + default: { + m_sBuffer.append(buff, iBytes); + break; + } + } + + if (bEOF) { + break; + } + } + + u_int iFind = m_sBuffer.find("\n"); + if (iFind != string::npos) { + return true; + } + + return !bEOF; +} + +int CFile::Write(const char *pszBuffer, u_int iBytes) { + if (m_iFD == -1) { + return -1; + } + + return write(m_iFD, pszBuffer, iBytes); +} + +int CFile::Write(const string & sData) { + return Write(sData.data(), sData.size()); +} +void CFile::Close() { close(m_iFD); m_iFD = -1; } + +string CFile::GetLongName() const { return m_sLongName; } +string CFile::GetShortName() const { return m_sShortName; } +void CFile::SetFD(int iFD) { m_iFD = iFD; } + + +#ifdef HAVE_LIBSSL +CBlowfish::CBlowfish(const string & sPassword, int iEncrypt, const string & sIvec) { + m_iEncrypt = iEncrypt; + m_ivec = (unsigned char *)calloc(sizeof(unsigned char), 8); + m_num = 0; + + if (sIvec.length() >= 8) { + memcpy(m_ivec, sIvec.data(), 8); + } + + BF_set_key(&m_bkey, sPassword.length(), (unsigned char *)sPassword.data()); +} + +CBlowfish::~CBlowfish() { + free(m_ivec); +} + +//! output must be freed +unsigned char *CBlowfish::MD5(const unsigned char *input, u_int ilen) { + unsigned char *output = (unsigned char *)malloc(MD5_DIGEST_LENGTH); + ::MD5(input, ilen, output); + return output; +} + +//! returns an md5 of the string (not hex encoded) +string CBlowfish::MD5(const string & sInput, bool bHexEncode) { + string sRet; + unsigned char *data = MD5((const unsigned char *)sInput.data(), sInput.length()); + + if (!bHexEncode) { + sRet.append((const char *)data, MD5_DIGEST_LENGTH); + } else { + for(int a = 0; a < MD5_DIGEST_LENGTH; a++) { + sRet += g_HexDigits[data[a] >> 4]; + sRet += g_HexDigits[data[a] & 0xf]; + } + } + + free(data); + return sRet; +} + +//! output must be the same size as input +void CBlowfish::Crypt(unsigned char *input, unsigned char *output, u_int ibytes) { + BF_cfb64_encrypt(input, output, ibytes, &m_bkey, m_ivec, &m_num, m_iEncrypt); +} + +//! must free result +unsigned char * CBlowfish::Crypt(unsigned char *input, u_int ibytes) { + unsigned char *buff = (unsigned char *)malloc(ibytes); + Crypt(input, buff, ibytes); + return buff; +} + +string CBlowfish::Crypt(const string & sData) { + unsigned char *buff = Crypt((unsigned char *)sData.data(), sData.length()); + string sOutput; + sOutput.append((const char *)buff, sData.length()); + free(buff); + return sOutput; +} + +#endif // HAVE_LIBSSL + diff --git a/Utils.h b/Utils.h new file mode 100644 index 00000000..cafd93cb --- /dev/null +++ b/Utils.h @@ -0,0 +1,291 @@ +#ifndef _UTILS_H +#define _UTILS_H + +#include +#include + +#include +#include +#include +using std::string; +using std::vector; +using std::map; + +#ifdef _DEBUG +#define DEBUG_ONLY(f) f +#else +#define DEBUG_ONLY(f) ((void)0) +#endif + +static const char g_HexDigits[] = "0123456789abcdef"; + +class CUtils +{ +public: + CUtils(); + virtual ~CUtils(); + + static string GetIP(unsigned long addr); + static unsigned long GetLongIP(const string& sIP); + static string ChangeDir(const string& sPath, const string& sAdd, const string& sHomeDir); + + static string ToString(short i); + static string ToString(unsigned short i); + static string ToString(int i); + static string ToString(unsigned int i); + static string ToString(long i); + static string ToString(unsigned long i); + static string ToString(unsigned long long i); + static string ToString(double i); + static string ToString(float i); + static string ToPercent(double d); + static string ToKBytes(double d); + + static string Left(const string& s, unsigned int u); + static string Right(const string& s, unsigned int u); + static string& Trim(string& s); + static string& LeftChomp(string& s, unsigned int uLen = 1); + static string& RightChomp(string& s, unsigned int uLen = 1); + static string Token(const string& s, unsigned int uPos, bool bRest = false, char cSep = ' '); + static string Ellipsize(const string& s, unsigned int uLen); + static bool WildCmp(const string& sWild, const string& sString); +private: +protected: +}; + + +class CTable : public vector* > { +public: + CTable(); + virtual ~CTable(); + + bool AddColumn(const string& sName); + unsigned int AddRow(); + bool SetCell(const string& sColumn, const string& sValue, unsigned int uRowIdx = ~0); + bool GetLine(unsigned int uIdx, string& sLine); + + unsigned int GetColumnWidth(unsigned int uIdx); +private: +protected: + vector m_vsHeaders; + map m_msuWidths; // Used to cache the width of a column +}; + + +class CFile { +public: + CFile(const string& sLongName); + virtual ~CFile(); + + enum EFileTypes + { + FT_REGULAR, + FT_DIRECTORY, + FT_CHARACTER, + FT_BLOCK, + FT_FIFO, + FT_LINK, + FT_SOCK + }; + + static bool IsReg( const string& sLongName, bool bUseLstat = false ); + static bool IsDir( const string& sLongName, bool bUseLstat = false ); + static bool IsChr( const string& sLongName, bool bUseLstat = false ); + static bool IsBlk( const string& sLongName, bool bUseLstat = false ); + static bool IsFifo( const string& sLongName, bool bUseLstat = false ); + static bool IsLnk( const string& sLongName, bool bUseLstat = true ); + static bool IsSock( const string& sLongName, bool bUseLstat = false ); + + bool IsReg( bool bUseLstat = false ); + bool IsDir( bool bUseLstat = false ); + bool IsChr( bool bUseLstat = false ); + bool IsBlk( bool bUseLstat = false ); + bool IsFifo( bool bUseLstat = false ); + bool IsLnk( bool bUseLstat = true ); + bool IsSock( bool bUseLstat = false ); + + bool access( int mode ); + + // for gettin file types, using fstat instead + static bool FType( const string sFileName, EFileTypes eType, bool bUseLstat = false ); + + enum EFileAttr { + FA_Name, + FA_Size, + FA_ATime, + FA_MTime, + FA_CTime, + FA_UID + }; + + // + // Functions to retrieve file information + // + bool Exists() const; + unsigned long long GetSize() const; + unsigned int GetATime() const; + unsigned int GetMTime() const; + unsigned int GetCTime() const; + int GetUID() const; + int GetGID() const; + static bool Exists(const string& sFile); + + static unsigned long long GetSize(const string& sFile); + static unsigned int GetATime(const string& sFile); + static unsigned int GetMTime(const string& sFile); + static unsigned int GetCTime(const string& sFile); + static int GetUID(const string& sFile); + static int GetGID(const string& sFile); + static int GetInfo(const string& sFile, struct stat& st); + + // + // Functions to manipulate the file on the filesystem + // + int Delete(); + int Move(const string& sNewFileName, bool bOverwrite = false); + + static bool Delete(const string& sFileName); + static bool Move(const string& sOldFileName, const string& sNewFileName, bool bOverwrite = false); + bool Chmod(mode_t mode); + static bool Chmod(const string& sFile, mode_t mode); + bool Seek(unsigned long uPos); + bool Open( int iFlags, mode_t iMode = 0644 ); + int Read( char *pszBuffer, int iBytes ); + bool ReadLine( string & sData ); + int Write( const char *pszBuffer, u_int iBytes ); + int Write( const string & sData ); + void Close(); + + string GetLongName() const; + string GetShortName() const; + void SetFD( int iFD ); + +private: + string m_sBuffer; + int m_iFD; + +protected: + string m_sLongName; //!< Absolute filename (m_sPath + "/" + m_sShortName) + string m_sShortName; //!< Filename alone, without path +}; + + +#ifdef HAVE_LIBSSL +#include +#include +//! does Blowfish w/64 bit feedback, no padding +class CBlowfish +{ +public: + /** + * @sPassword key to encrypt with + * @iEncrypt encrypt method (BF_DECRYPT or BF_ENCRYPT) + * @sIvec what to set the ivector to start with, default sets it all 0's + */ + CBlowfish(const string & sPassword, int iEncrypt, const string & sIvec = ""); + ~CBlowfish(); + + //! output must be freed + static unsigned char *MD5(const unsigned char *input, u_int ilen); + + //! returns an md5 of the string ( not hex encoded ) + static string MD5(const string & sInput, bool bHexEncode = false); + + //! output must be the same size as input + void Crypt(unsigned char *input, unsigned char *output, u_int ibytes); + + //! must free result + unsigned char * Crypt(unsigned char *input, u_int ibytes); + string Crypt(const string & sData); + +private: + unsigned char *m_ivec; + BF_KEY m_bkey; + int m_iEncrypt, m_num; +}; + +#endif /* HAVE_LIBSSL */ + +#define RF_BUFF 4096 +inline bool ReadFile(const string & sFilename, string & sLine) { + char inbuff[RF_BUFF]; + int bytes; + // clear ourselves out + sLine.clear(); + + FILE *f = fopen(sFilename.c_str(), "r"); + + if (!f) { + return false; + } + + while((bytes = fread(inbuff, sizeof(char), RF_BUFF, f)) > 0) { + sLine.append(inbuff, bytes); + } + + fclose(f); + if (bytes < 0) { + return false; + } + + return true; +} + +inline bool WriteFile(const string & sFilename, const string & sData) { + FILE *f = fopen(sFilename.c_str(), "w"); + if (!f) { + return false; + } + + int iRet = fwrite(sData.data(), sizeof(char), sData.length(), f); + + fclose(f); + + if (iRet <= 0) { + return false; + } + + return true; +} + +inline bool ReadLine(const string & sData, string & sLine, u_int & iPos) { + sLine.clear(); + + if (iPos >= sData.length()) { + return false; + } + + u_int iFind = sData.find("\n", iPos); + + if (iFind == string::npos) { + sLine = sData.substr(iPos, (sData.length() - iPos)); + iPos = string::npos; + return true; + } + + sLine = sData.substr(iPos, (iFind - iPos)) + "\n"; + iPos = iFind + 1; + + return true; +} + +inline string Lower(const string & sLine) { + string sRet; + for(u_int a = 0; a < sLine.length(); a++) { + sRet += tolower(sLine[a]); + } + + return sRet; +} + +inline string Upper(const string & sLine) { + string sRet; + for(u_int a = 0; a < sLine.length(); a++) { + sRet += toupper(sLine[a]); + } + + return sRet; +} + +#endif // !_UTILS_H + diff --git a/main.cpp b/main.cpp new file mode 100644 index 00000000..af19d5b2 --- /dev/null +++ b/main.cpp @@ -0,0 +1,92 @@ +#include +#include +#include +#include "znc.h" +#include "md5.h" + +void die(int sig) { + signal( SIGSEGV, SIG_DFL ); + signal( SIGABRT, SIG_DFL ); + signal( SIGPIPE, SIG_DFL ); + + delete CZNC::New(); + exit(sig); +} + +int main(int argc, char** argv) { + string sConfig = "znc.conf"; + + if (argc > 1) { + if ((argc > 2) || (strcasecmp(argv[1], "--help") == 0) || (strcasecmp(argv[1], "-h") == 0)) { + cerr << "Usage: " << argv[0] << " [--makepass|--help|znc.conf]" << endl; + return 1; + } + + if (strcasecmp(argv[1], "--makepass") == 0) { + char* pass = getpass( "Enter Password: " ); + int iLen = strlen(pass); + cout << "Use this in the section of your config:" << endl << endl << "Pass = " << CMD5(pass, iLen) << " -" << endl << endl; + memset((char*) pass, 0, iLen); // Null out our pass so it doesn't sit in memory + return 0; + } else { + sConfig = argv[1]; + } + } + + CZNC* pZNC = CZNC::New(); + + pZNC->InitDirs(((argc) ? argv[0] : "")); + + if (!pZNC->ParseConfig(sConfig)) { + cerr << endl << "*** Unrecoverable error while parsing [" << sConfig << "]" << endl; + delete pZNC; + return 1; + } + + if (!pZNC->GetListenPort()) { + cerr << "You must supply a ListenPort in your config." << endl; + delete pZNC; + return 1; + } + + if (!pZNC->OnBoot()) { + cerr << "Exiting due to module boot errors." << endl; + delete pZNC; + return 1; + } + +#ifndef _DEBUG + int iPid = fork(); + if (iPid == -1) { + cerr << "Failed to fork into background: [" << strerror(errno) << "]" << endl; + delete pZNC; + exit(1); + } + + if (iPid > 0) { + cout << "ZNC - by prozac [port: " << ((pZNC->IsSSL()) ? "+" : "") << pZNC->GetListenPort() << "] [pid: " << iPid << "]" << endl; + pZNC->WritePidFile(iPid); + exit(0); + } +#endif + + struct sigaction sa; + sa.sa_flags = 0; + sigemptyset(&sa.sa_mask); + + sa.sa_handler = SIG_IGN; + sigaction(SIGPIPE, &sa, (struct sigaction *)NULL); + + sa.sa_handler = die; + sigaction(SIGINT, &sa, (struct sigaction *)NULL); + sigaction(SIGILL, &sa, (struct sigaction *)NULL); + sigaction(SIGQUIT, &sa, (struct sigaction *)NULL); + sigaction(SIGBUS, &sa, (struct sigaction *)NULL); + sigaction(SIGSEGV, &sa, (struct sigaction *)NULL); + sigaction(SIGTERM, &sa, (struct sigaction *)NULL); + + int iRet = pZNC->Loop(); + delete pZNC; + + return iRet; +} diff --git a/main.h b/main.h new file mode 100644 index 00000000..a8ae971e --- /dev/null +++ b/main.h @@ -0,0 +1,17 @@ +#ifndef _MAIN_H +#define _MAIN_H + + +#ifndef CS_STRING +#define CS_STRING string +#endif + +#ifndef _NO_CSOCKET_NS +#define _NO_CSOCKET_NS +#endif + +#include +using std::string; +#include "Csocket.h" + +#endif // !_MAIN_H diff --git a/md5.cpp b/md5.cpp new file mode 100644 index 00000000..cee72c84 --- /dev/null +++ b/md5.cpp @@ -0,0 +1,283 @@ +/* + * RFC 1321 compliant MD5 implementation + * + * Copyright (C) 2001-2003 Christophe Devine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include + +#include "md5.h" + +CMD5::CMD5() +{ + *m_szMD5 = '\0'; +} + +CMD5::CMD5(const string& sText) +{ + MakeHash(sText.c_str(), sText.length()); +} + +CMD5::CMD5(const char* szText, uint32 nTextLen) +{ + MakeHash(szText, nTextLen); +} + +CMD5::~CMD5() +{ +} + +#define GET_UINT32(n,b,i) \ +{ \ + (n) = ( (uint32) (b)[(i) ] ) \ + | ( (uint32) (b)[(i) + 1] << 8 ) \ + | ( (uint32) (b)[(i) + 2] << 16 ) \ + | ( (uint32) (b)[(i) + 3] << 24 ); \ +} + +#define PUT_UINT32(n,b,i) \ +{ \ + (b)[(i) ] = (uint8) ( (n) ); \ + (b)[(i) + 1] = (uint8) ( (n) >> 8 ); \ + (b)[(i) + 2] = (uint8) ( (n) >> 16 ); \ + (b)[(i) + 3] = (uint8) ( (n) >> 24 ); \ +} + +void CMD5::md5_starts( md5_context *ctx ) const +{ + ctx->total[0] = 0; + ctx->total[1] = 0; + + ctx->state[0] = 0x67452301; + ctx->state[1] = 0xEFCDAB89; + ctx->state[2] = 0x98BADCFE; + ctx->state[3] = 0x10325476; +} + +void CMD5::md5_process( md5_context *ctx, uint8 data[64] ) const +{ + uint32 X[16], A, B, C, D; + + GET_UINT32( X[0], data, 0 ); + GET_UINT32( X[1], data, 4 ); + GET_UINT32( X[2], data, 8 ); + GET_UINT32( X[3], data, 12 ); + GET_UINT32( X[4], data, 16 ); + GET_UINT32( X[5], data, 20 ); + GET_UINT32( X[6], data, 24 ); + GET_UINT32( X[7], data, 28 ); + GET_UINT32( X[8], data, 32 ); + GET_UINT32( X[9], data, 36 ); + GET_UINT32( X[10], data, 40 ); + GET_UINT32( X[11], data, 44 ); + GET_UINT32( X[12], data, 48 ); + GET_UINT32( X[13], data, 52 ); + GET_UINT32( X[14], data, 56 ); + GET_UINT32( X[15], data, 60 ); + +#define S(x,n) ((x << n) | ((x & 0xFFFFFFFF) >> (32 - n))) + +#define P(a,b,c,d,k,s,t) \ +{ \ + a += F(b,c,d) + X[k] + t; a = S(a,s) + b; \ +} + + A = ctx->state[0]; + B = ctx->state[1]; + C = ctx->state[2]; + D = ctx->state[3]; + +#define F(x,y,z) (z ^ (x & (y ^ z))) + + P( A, B, C, D, 0, 7, 0xD76AA478 ); + P( D, A, B, C, 1, 12, 0xE8C7B756 ); + P( C, D, A, B, 2, 17, 0x242070DB ); + P( B, C, D, A, 3, 22, 0xC1BDCEEE ); + P( A, B, C, D, 4, 7, 0xF57C0FAF ); + P( D, A, B, C, 5, 12, 0x4787C62A ); + P( C, D, A, B, 6, 17, 0xA8304613 ); + P( B, C, D, A, 7, 22, 0xFD469501 ); + P( A, B, C, D, 8, 7, 0x698098D8 ); + P( D, A, B, C, 9, 12, 0x8B44F7AF ); + P( C, D, A, B, 10, 17, 0xFFFF5BB1 ); + P( B, C, D, A, 11, 22, 0x895CD7BE ); + P( A, B, C, D, 12, 7, 0x6B901122 ); + P( D, A, B, C, 13, 12, 0xFD987193 ); + P( C, D, A, B, 14, 17, 0xA679438E ); + P( B, C, D, A, 15, 22, 0x49B40821 ); + +#undef F + +#define F(x,y,z) (y ^ (z & (x ^ y))) + + P( A, B, C, D, 1, 5, 0xF61E2562 ); + P( D, A, B, C, 6, 9, 0xC040B340 ); + P( C, D, A, B, 11, 14, 0x265E5A51 ); + P( B, C, D, A, 0, 20, 0xE9B6C7AA ); + P( A, B, C, D, 5, 5, 0xD62F105D ); + P( D, A, B, C, 10, 9, 0x02441453 ); + P( C, D, A, B, 15, 14, 0xD8A1E681 ); + P( B, C, D, A, 4, 20, 0xE7D3FBC8 ); + P( A, B, C, D, 9, 5, 0x21E1CDE6 ); + P( D, A, B, C, 14, 9, 0xC33707D6 ); + P( C, D, A, B, 3, 14, 0xF4D50D87 ); + P( B, C, D, A, 8, 20, 0x455A14ED ); + P( A, B, C, D, 13, 5, 0xA9E3E905 ); + P( D, A, B, C, 2, 9, 0xFCEFA3F8 ); + P( C, D, A, B, 7, 14, 0x676F02D9 ); + P( B, C, D, A, 12, 20, 0x8D2A4C8A ); + +#undef F + +#define F(x,y,z) (x ^ y ^ z) + + P( A, B, C, D, 5, 4, 0xFFFA3942 ); + P( D, A, B, C, 8, 11, 0x8771F681 ); + P( C, D, A, B, 11, 16, 0x6D9D6122 ); + P( B, C, D, A, 14, 23, 0xFDE5380C ); + P( A, B, C, D, 1, 4, 0xA4BEEA44 ); + P( D, A, B, C, 4, 11, 0x4BDECFA9 ); + P( C, D, A, B, 7, 16, 0xF6BB4B60 ); + P( B, C, D, A, 10, 23, 0xBEBFBC70 ); + P( A, B, C, D, 13, 4, 0x289B7EC6 ); + P( D, A, B, C, 0, 11, 0xEAA127FA ); + P( C, D, A, B, 3, 16, 0xD4EF3085 ); + P( B, C, D, A, 6, 23, 0x04881D05 ); + P( A, B, C, D, 9, 4, 0xD9D4D039 ); + P( D, A, B, C, 12, 11, 0xE6DB99E5 ); + P( C, D, A, B, 15, 16, 0x1FA27CF8 ); + P( B, C, D, A, 2, 23, 0xC4AC5665 ); + +#undef F + +#define F(x,y,z) (y ^ (x | ~z)) + + P( A, B, C, D, 0, 6, 0xF4292244 ); + P( D, A, B, C, 7, 10, 0x432AFF97 ); + P( C, D, A, B, 14, 15, 0xAB9423A7 ); + P( B, C, D, A, 5, 21, 0xFC93A039 ); + P( A, B, C, D, 12, 6, 0x655B59C3 ); + P( D, A, B, C, 3, 10, 0x8F0CCC92 ); + P( C, D, A, B, 10, 15, 0xFFEFF47D ); + P( B, C, D, A, 1, 21, 0x85845DD1 ); + P( A, B, C, D, 8, 6, 0x6FA87E4F ); + P( D, A, B, C, 15, 10, 0xFE2CE6E0 ); + P( C, D, A, B, 6, 15, 0xA3014314 ); + P( B, C, D, A, 13, 21, 0x4E0811A1 ); + P( A, B, C, D, 4, 6, 0xF7537E82 ); + P( D, A, B, C, 11, 10, 0xBD3AF235 ); + P( C, D, A, B, 2, 15, 0x2AD7D2BB ); + P( B, C, D, A, 9, 21, 0xEB86D391 ); + +#undef F + + ctx->state[0] += A; + ctx->state[1] += B; + ctx->state[2] += C; + ctx->state[3] += D; +} + +void CMD5::md5_update( md5_context *ctx, uint8 *input, uint32 length ) const +{ + uint32 left, fill; + + if( ! length ) return; + + left = ctx->total[0] & 0x3F; + fill = 64 - left; + + ctx->total[0] += length; + ctx->total[0] &= 0xFFFFFFFF; + + if( ctx->total[0] < length ) + ctx->total[1]++; + + if( left && length >= fill ) + { + memcpy( (void *) (ctx->buffer + left), + (void *) input, fill ); + md5_process( ctx, ctx->buffer ); + length -= fill; + input += fill; + left = 0; + } + + while( length >= 64 ) + { + md5_process( ctx, input ); + length -= 64; + input += 64; + } + + if( length ) + { + memcpy( (void *) (ctx->buffer + left), + (void *) input, length ); + } +} + +static uint8 md5_padding[64] = +{ + 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +void CMD5::md5_finish( md5_context *ctx, uint8 digest[16] ) const +{ + uint32 last, padn; + uint32 high, low; + uint8 msglen[8]; + + high = ( ctx->total[0] >> 29 ) + | ( ctx->total[1] << 3 ); + low = ( ctx->total[0] << 3 ); + + PUT_UINT32( low, msglen, 0 ); + PUT_UINT32( high, msglen, 4 ); + + last = ctx->total[0] & 0x3F; + padn = ( last < 56 ) ? ( 56 - last ) : ( 120 - last ); + + md5_update( ctx, md5_padding, padn ); + md5_update( ctx, msglen, 8 ); + + PUT_UINT32( ctx->state[0], digest, 0 ); + PUT_UINT32( ctx->state[1], digest, 4 ); + PUT_UINT32( ctx->state[2], digest, 8 ); + PUT_UINT32( ctx->state[3], digest, 12 ); +} + +char* CMD5::MakeHash(const char* szText, uint32 nTextLen) +{ + md5_context ctx; + unsigned char md5sum[16]; + unsigned short i; + + md5_starts(&ctx); + md5_update(&ctx, (uint8*)szText, nTextLen); + md5_finish(&ctx, md5sum); + + for (i = 0; i < 16; i++) + { + sprintf(m_szMD5 + i * 2, "%02x", md5sum[i]); + } + + return(m_szMD5); +} diff --git a/md5.h b/md5.h new file mode 100644 index 00000000..8e32896f --- /dev/null +++ b/md5.h @@ -0,0 +1,56 @@ +/* C implementation by Christophe Devine, C++ "class-ified" by [T3] */ + +#include +using std::string; + +#ifndef _MD5_H +#define _MD5_H + +#ifndef uint8 +#define uint8 unsigned char +#endif + +#ifndef uint32 +#define uint32 unsigned long int +#endif + +typedef struct +{ + uint32 total[2]; + uint32 state[4]; + uint8 buffer[64]; +} +md5_context; + +class CMD5 { +protected: + char m_szMD5[33]; + +public: + CMD5(); + CMD5(const string& sText); + CMD5(const char* szText, uint32 nTextLen); + virtual ~CMD5(); + + operator string() const + { + return (string) m_szMD5; + } + + operator char*() const + { + return (char*)m_szMD5; + } + + char* MakeHash(const char* szText, uint32 nTextLen); + +protected: + void md5_starts( md5_context *ctx ) const; + void md5_update( md5_context *ctx, uint8 *input, uint32 length ) const; + void md5_finish( md5_context *ctx, uint8 digest[16] ) const; + +private: + void md5_process( md5_context *ctx, uint8 data[64] ) const; +}; + +#endif /* _MD5_H */ diff --git a/modules/buildmod b/modules/buildmod new file mode 100755 index 00000000..5a5c813d --- /dev/null +++ b/modules/buildmod @@ -0,0 +1,45 @@ +#!/bin/sh +CXXFLAGS="-Wall -D_GNU_SOURCE -DHAVE_LIBSSL -I.." +LIBS="-lssl" + +if test -z "$1"; then + echo "Usage: $0 [...]" + exit 1 +fi + +for arg in "$@" +do + if test -d $arg; then + (cd $arg && make || exit 1) + elif test -f "$arg"; then + FILE="$arg" + MOD="${FILE%.cpp}" + MOD="${MOD%.cc}" + elif test -f "$arg.cpp"; then + FILE="$arg.cpp" + MOD="$arg" + elif test -f "$arg.cc"; then + FILE="$arg.cc" + MOD="$arg" + fi + + rm -f "$MOD.so" + + if test -n "$FILE"; then + COMMAND="g++ $CXXFLAGS -c $FILE" + echo $COMMAND + $COMMAND || exit 1 + COMMAND="g++ $CXXFLAGS -shared -o $MOD.so $MOD.o $LIBS" + echo $COMMAND + $COMMAND || exit 1 + fi + + if ! test -f "$MOD.so"; then + echo "Failed to build $MOD.so!" + exit 1 + else + echo "Built $MOD.so" + fi +done + +exit 0 diff --git a/modules/email.cc b/modules/email.cc new file mode 100644 index 00000000..14731034 --- /dev/null +++ b/modules/email.cc @@ -0,0 +1,269 @@ +#include "main.h" +#include "User.h" +#include "Nick.h" +#include "Modules.h" +#include "Chan.h" +#include "Utils.h" +#include "md5.h" +#include +#include + + +/* + * Email Monitor / Retrieval + * Author: imaginos + * + * $Log$ + * Revision 1.1 2004/08/24 00:08:52 prozacx + * Initial revision + * + * + */ + +struct EmailST +{ + string sFrom; + string sSubject; + string sUidl; + u_int iSize; +}; + +class CEmailJob : public CTimer +{ +public: + CEmailJob( CModule* pModule, unsigned int uInterval, unsigned int uCycles, const string& sLabel, const string& sDescription ) + : CTimer( pModule, uInterval, uCycles, sLabel, sDescription) {} + + virtual ~CEmailJob() {} + +protected: + virtual void RunJob(); +}; + +class CEmail : public CModule +{ +public: + MODCONSTRUCTOR(CEmail) + { + m_iLastCheck = 0; + m_bInitialized = false; + } + virtual ~CEmail() + { + vector vSocks = m_pManager->FindSocksByName( "EMAIL::" + m_pUser->GetUserName() ); + for( u_int a = 0; a < vSocks.size(); a++ ) + m_pManager->DelSockByAddr( vSocks[a] ); + } + + virtual bool OnLoad(const string & sArgs) { + m_sMailPath = sArgs; + + StartParser(); + if ( m_pUser->IsUserAttached() ) + StartTimer(); + + return true; + } + + virtual void OnUserAttached() + { + stringstream s; + s << "You have " << m_ssUidls.size() << " emails."; + PutModule( s.str() ); + StartTimer(); + } + virtual void OnUserDetached() + { + RemTimer( "EMAIL::" + m_pUser->GetUserName() ); + } + + void StartTimer() + { + if ( !FindTimer( "EMAIL::" + m_pUser->GetUserName() ) ) + { + CEmailJob *p = new CEmailJob( this, 60, 0, "EmailMonitor", "Monitors email activity" ); + AddTimer( p ); + } + } + + virtual string GetDescription() + { + return ( "Monitors Email activity on local disk /var/mail/user" ); + } + + virtual void OnModCommand( const string& sCommand ); + void StartParser(); + + void ParseEmails( const vector & vEmails ) + { + if ( !m_bInitialized ) + { + m_bInitialized = true; + for( u_int a = 0; a < vEmails.size(); a++ ) + m_ssUidls.insert( vEmails[a].sUidl ); + + stringstream s; + s << "You have " << vEmails.size() << " emails."; + PutModule( s.str() ); + } else + { + set ssUidls; + + CTable Table; + Table.AddColumn("From"); + Table.AddColumn("Size"); + Table.AddColumn("Subject"); + + for( u_int a = 0; a < vEmails.size(); a++ ) + { + if ( m_ssUidls.find( vEmails[a].sUidl ) == m_ssUidls.end() ) + { + //PutModule( "------------------- New Email -------------------" ); + Table.AddRow(); + Table.SetCell( "From", CUtils::Ellipsize( vEmails[a].sFrom, 32 ) ); + Table.SetCell( "Size", CUtils::ToString( vEmails[a].iSize ) ); + Table.SetCell( "Subject", CUtils::Ellipsize( vEmails[a].sSubject, 64 ) ); + } + ssUidls.insert( vEmails[a].sUidl ); + } + + m_ssUidls = ssUidls; // keep the list in synch + + if (Table.size()) { + unsigned int uTableIdx = 0; + string sLine; + while ( Table.GetLine( uTableIdx++, sLine ) ) { + PutModule( sLine ); + } + + stringstream s; + s << "You have " << vEmails.size() << " emails."; + PutModule( s.str() ); + } + } + } + + +private: + string m_sMailPath; + u_int m_iLastCheck; + set m_ssUidls; + bool m_bInitialized; +}; + +class CEmailFolder : public Csock +{ +public: + CEmailFolder( CEmail *pModule, const string & sMailbox ) : Csock() + { + m_pModule = pModule; + m_sMailbox = sMailbox; + EnableReadLine(); + } + + virtual ~CEmailFolder() + { + if ( !m_sMailBuffer.empty() ) + ProcessMail(); // get the last one + + if ( !m_vEmails.empty() ) + m_pModule->ParseEmails( m_vEmails ); + } + + virtual void ReadLine( const CS_STRING & sLine ) + { + if ( sLine.substr( 0, 5 ) == "From " ) + { + if ( !m_sMailBuffer.empty() ) + { + ProcessMail(); + m_sMailBuffer.clear(); + } + } + m_sMailBuffer += sLine; + } + + void ProcessMail() + { + EmailST tmp; + tmp.sUidl = (char *)CMD5( m_sMailBuffer.substr( 0, 255 ) ); + string sLine; + u_int iPos = 0; + while( ::ReadLine( m_sMailBuffer, sLine, iPos ) ) + { + CUtils::Trim( sLine ); + if ( sLine.empty() ) + break; // out of the headers + + if ( strncasecmp( sLine.c_str(), "From: ", 6 ) == 0 ) + tmp.sFrom = sLine.substr( 6, string::npos ); + else if ( strncasecmp( sLine.c_str(), "Subject: ", 9 ) == 0 ) + tmp.sSubject = sLine.substr( 9, string::npos ); + + if ( ( !tmp.sFrom.empty() ) && ( !tmp.sSubject.empty() ) ) + break; + } + tmp.iSize = m_sMailBuffer.length(); + m_vEmails.push_back( tmp ); + } +private: + CEmail *m_pModule; + string m_sMailbox; + string m_sMailBuffer; + vector m_vEmails; +}; + +void CEmail::OnModCommand( const string& sCommand ) +{ + u_int iPos = sCommand.find( " " ); + string sCom, sArgs; + if ( iPos == string::npos ) + sCom = sCommand; + else + { + sCom = sCommand.substr( 0, iPos ); + sArgs = sCommand.substr( iPos + 1, string::npos ); + } + + if ( sCom == "timers" ) + { + ListTimers(); + } else + PutModule( "Error, no such command [" + sCom + "]" ); +} + +void CEmail::StartParser() +{ + string sParserName = "EMAIL::" + m_pUser->GetUserName(); + + if ( m_pManager->FindSockByName( sParserName ) ) + return; // one at a time sucker + + CFile cFile( m_sMailPath ); + if ( ( !cFile.Exists() ) || ( cFile.GetSize() == 0 ) ) + { + m_bInitialized = true; + return; // der + } + + if ( cFile.GetMTime() <= m_iLastCheck ) + return; // only check if modified + + int iFD = open( m_sMailPath.c_str(), O_RDONLY ); + if ( iFD >= 0 ) + { + m_iLastCheck = time( NULL ); + CEmailFolder *p = new CEmailFolder( this, m_sMailPath ); + p->SetRSock( iFD ); + p->SetWSock( iFD ); + m_pManager->AddSock( (Csock *)p, "EMAIL::" + m_pUser->GetUserName() ); + } +} + +void CEmailJob::RunJob() +{ + CEmail *p = (CEmail *)m_pModule; + p->StartParser(); +} +MODULEDEFS(CEmail) + diff --git a/modules/raw.cpp b/modules/raw.cpp new file mode 100644 index 00000000..9eb1cda6 --- /dev/null +++ b/modules/raw.cpp @@ -0,0 +1,28 @@ +#include "main.h" +#include "User.h" +#include "Nick.h" +#include "Modules.h" +#include "Chan.h" + +class CRawMod : public CModule { +public: + MODCONSTRUCTOR(CRawMod) {} + virtual ~CRawMod() {} + + virtual string GetDescription() { + return "View all of the raw traffic."; + } + + virtual bool OnRaw(string& sLine) { + PutModule("IRC -> [" + sLine + "]"); + return false; + } + + virtual bool OnUserRaw(string& sLine) { + PutModule("YOU -> [" + sLine + "]"); + return false; + } +}; + +MODULEDEFS(CRawMod) + diff --git a/modules/sample.cpp b/modules/sample.cpp new file mode 100644 index 00000000..8a822a81 --- /dev/null +++ b/modules/sample.cpp @@ -0,0 +1,198 @@ +#include "main.h" +#include "User.h" +#include "Nick.h" +#include "Modules.h" +#include "Chan.h" + +class CSampleTimer : public CTimer { +public: + + CSampleTimer(CModule* pModule, unsigned int uInterval, unsigned int uCycles, const string& sLabel, const string& sDescription) : CTimer(pModule, uInterval, uCycles, sLabel, sDescription) {} + virtual ~CSampleTimer() {} + +private: +protected: + virtual void RunJob() { + m_pModule->PutModule("TEST!!!!"); + } +}; + +class CSampleMod : public CModule { +public: + MODCONSTRUCTOR(CSampleMod) {} + + virtual bool OnLoad(const string& sArgs) { + PutModule("I'm being loaded with the arguments: [" + sArgs + "]"); + //AddTimer(new CSampleTimer(this, 300, 0, "Sample", "Sample timer for sample things.")); + //AddTimer(new CSampleTimer(this, 5, 20, "Another", "Another sample timer.")); + //AddTimer(new CSampleTimer(this, 25000, 5, "Third", "A third sample timer.")); + return true; + } + + virtual ~CSampleMod() { + PutModule("I'm being unloaded!"); + } + + virtual bool OnBoot() { + // This is called when the app starts up (only modules that are loaded in the config will get this event) + return true; + } + + virtual string GetDescription() { + return "To be used as a sample for writing modules."; + } + + virtual void OnIRCConnected() { + PutModule("You got connected BoyOh."); + } + + virtual void OnIRCDisconnected() { + PutModule("You got disconnected BoyOh."); + } + + virtual void OnOp(const CNick& OpNick, const CNick& Nick, const CChan& Channel, bool bNoChange) { + PutModule(((bNoChange) ? "[0] " : "[1] [") + OpNick.GetNick() + "] opped [" + Nick.GetNick() + "] on [" + Channel.GetName() + "]"); + } + + virtual void OnDeop(const CNick& OpNick, const CNick& Nick, const CChan& Channel, bool bNoChange) { + PutModule(((bNoChange) ? "[0] " : "[1] [") + OpNick.GetNick() + "] deopped [" + Nick.GetNick() + "] on [" + Channel.GetName() + "]"); + } + + virtual void OnVoice(const CNick& OpNick, const CNick& Nick, const CChan& Channel, bool bNoChange) { + PutModule(((bNoChange) ? "[0] " : "[1] [") + OpNick.GetNick() + "] voiced [" + Nick.GetNick() + "] on [" + Channel.GetName() + "]"); + } + + virtual void OnDevoice(const CNick& OpNick, const CNick& Nick, const CChan& Channel, bool bNoChange) { + PutModule(((bNoChange) ? "[0] " : "[1] [") + OpNick.GetNick() + "] devoiced [" + Nick.GetNick() + "] on [" + Channel.GetName() + "]"); + } + + virtual void OnRawMode(const CNick& OpNick, const CChan& Channel, const string& sModes, const string& sArgs) { + PutModule("* " + OpNick.GetNick() + " sets mode: " + sModes + " " + sArgs + " (" + Channel.GetName() + ")"); + } + + virtual bool OnRaw(string& sLine) { + // PutModule("OnRaw() [" + sLine + "]"); + return false; + } + + virtual bool OnUserRaw(string& sLine) { + // PutModule("UserRaw() [" + sLine + "]"); + return false; + } + + virtual void OnKick(const CNick& OpNick, const string& sKickedNick, const CChan& Channel, const string& sMessage) { + PutModule("[" + OpNick.GetNick() + "] kicked [" + sKickedNick + "] from [" + Channel.GetName() + "] with the msg [" + sMessage + "]"); + } + + virtual void OnQuit(const CNick& Nick, const string& sMessage) { + PutModule("* Quits: " + Nick.GetNick() + " (" + Nick.GetIdent() + "!" + Nick.GetHost() + ") (" + sMessage + ")"); + } + + virtual void OnJoin(const CNick& Nick, const CChan& Channel) { + PutModule("* Joins: " + Nick.GetNick() + " (" + Nick.GetIdent() + "!" + Nick.GetHost() + ")"); + } + + virtual void OnPart(const CNick& Nick, const CChan& Channel) { + PutModule("* Parts: " + Nick.GetNick() + " (" + Nick.GetIdent() + "!" + Nick.GetHost() + ")"); + } + + virtual void OnNick(const CNick& OldNick, const string& sNewNick) { + PutModule("* " + OldNick.GetNick() + " is now known as " + sNewNick); + } + + virtual bool OnUserCTCPReply(const CNick& Nick, string& sMessage) { + PutModule("[" + Nick.GetNick() + "] userctcpreply [" + sMessage + "]"); + sMessage = "\037" + sMessage + "\037"; + + return false; + } + + virtual bool OnCTCPReply(const CNick& Nick, string& sMessage) { + PutModule("[" + Nick.GetNick() + "] ctcpreply [" + sMessage + "]"); + + return false; + } + + virtual bool OnUserCTCP(const string& sTarget, string& sMessage) { + PutModule("[" + sTarget + "] userctcp [" + sMessage + "]"); + + return false; + } + + virtual bool OnPrivCTCP(const CNick& Nick, string& sMessage) { + PutModule("[" + Nick.GetNick() + "] privctcp [" + sMessage + "]"); + sMessage = "\002" + sMessage + "\002"; + + return false; + } + + virtual bool OnChanCTCP(const CNick& Nick, const CChan& Channel, string& sMessage) { + PutModule("[" + Nick.GetNick() + "] chanctcp [" + sMessage + "] to [" + Channel.GetName() + "]"); + sMessage = "\00311,5 " + sMessage + " \003"; + + return false; + } + + virtual bool OnUserNotice(const string& sTarget, string& sMessage) { + PutModule("[" + sTarget + "] usernotice [" + sMessage + "]"); + sMessage = "\037" + sMessage + "\037"; + + return false; + } + + virtual bool OnPrivNotice(const CNick& Nick, string& sMessage) { + PutModule("[" + Nick.GetNick() + "] privnotice [" + sMessage + "]"); + sMessage = "\002" + sMessage + "\002"; + + return false; + } + + virtual bool OnChanNotice(const CNick& Nick, const CChan& Channel, string& sMessage) { + PutModule("[" + Nick.GetNick() + "] channotice [" + sMessage + "] to [" + Channel.GetName() + "]"); + sMessage = "\00311,5 " + sMessage + " \003"; + + return false; + } + + virtual bool OnUserMsg(const string& sTarget, string& sMessage) { + PutModule("[" + sTarget + "] usermsg [" + sMessage + "]"); + sMessage = "\0034" + sMessage + "\003"; + + return false; + } + + virtual bool OnPrivMsg(const CNick& Nick, string& sMessage) { + PutModule("[" + Nick.GetNick() + "] privmsg [" + sMessage + "]"); + sMessage = "\002" + sMessage + "\002"; + + return false; + } + + virtual bool OnChanMsg(const CNick& Nick, const CChan& Channel, string& sMessage) { + if (sMessage == "!ping") { + PutIRC("PRIVMSG " + Channel.GetName() + " :PONG?"); + } + + sMessage = "x " + sMessage + " x"; + + return false; + } + + virtual void OnModCommand(const string& sCommand) { + if (strcasecmp(sCommand.c_str(), "TIMERS") == 0) { + ListTimers(); + } + } + + virtual bool OnStatusCommand(const string& sCommand) { + if (strcasecmp(sCommand.c_str(), "SAMPLE") == 0) { + PutModule("Hi, I'm your friendly sample module."); + return true; + } + + return false; + } +}; + +MODULEDEFS(CSampleMod) + diff --git a/modules/savebuff.cc b/modules/savebuff.cc new file mode 100644 index 00000000..38824225 --- /dev/null +++ b/modules/savebuff.cc @@ -0,0 +1,211 @@ +#include "main.h" +#include "User.h" +#include "Nick.h" +#include "Modules.h" +#include "Chan.h" +#include "Utils.h" +#include + +#ifndef HAVE_LIBSSL +#error This plugin only works with OpenSSL +#endif /* HAVE_LIBSSL */ + +#define CRYPT_VERIFICATION_TOKEN "::__:SAVEBUFF:__::" + +/* + * Buffer Saving thing, incase your shit goes out while your out + * Author: imaginos + * + * Its only as secure as your shell, the encryption only offers a slightly + * better solution then plain text. + * + * $Log$ + * Revision 1.1 2004/08/24 00:08:52 prozacx + * Initial revision + * + * + */ + +class CSaveBuff; + +class CSaveBuffJob : public CTimer +{ +public: + CSaveBuffJob( CModule* pModule, unsigned int uInterval, unsigned int uCycles, const string& sLabel, const string& sDescription ) + : CTimer( pModule, uInterval, uCycles, sLabel, sDescription) {} + + virtual ~CSaveBuffJob() {} + +protected: + virtual void RunJob(); +}; + +class CSaveBuff : public CModule +{ +public: + MODCONSTRUCTOR(CSaveBuff) + { + // m_sPassword = CBlowfish::MD5( "" ); + AddTimer( new CSaveBuffJob( this, 60, 0, "SaveBuff", "Saves the current buffer to disk every 1 minute" ) ); + } + virtual ~CSaveBuff() + { + SaveBufferToDisk(); + } + + virtual bool OnBoot() + { + if ( m_sPassword.empty() ) + { + char *pTmp = getpass( "Enter Encryption Key for savebuff.so: " ); + + if ( pTmp ) + m_sPassword = CBlowfish::MD5( pTmp ); + + *pTmp = 0; + } + + const vector& vChans = m_pUser->GetChans(); + for( u_int a = 0; a < vChans.size(); a++ ) + { + string sFile; + string sPath = GetPath( vChans[a]->GetName() ); + + if ( ( sPath.empty() ) || ( !ReadFile( sPath, sFile ) ) ) + continue; + if ( !sFile.empty() ) + { + CBlowfish c( m_sPassword, BF_DECRYPT ); + sFile = c.Crypt( sFile ); + + if ( sFile.substr( 0, strlen( CRYPT_VERIFICATION_TOKEN ) ) != CRYPT_VERIFICATION_TOKEN ) + { + // failed to decode :( + cerr << "Unable to decode Encrypted file [" << sPath << "]" << endl; + continue; + } + sFile.erase( 0, strlen( CRYPT_VERIFICATION_TOKEN ) ); + + string sLine; + u_int iPos = 0; + while( ReadLine( sFile, sLine, iPos ) ) + { + CUtils::Trim( sLine ); + vChans[a]->AddBuffer( sLine ); + } + } + } + + return true; + } + void SaveBufferToDisk() + { + if ( !m_sPassword.empty() ) + { + const vector& vChans = m_pUser->GetChans(); + for( u_int a = 0; a < vChans.size(); a++ ) + { + const vector & vBuffer = vChans[a]->GetBuffer(); + if ( vBuffer.empty() ) + continue; + + string sFile = CRYPT_VERIFICATION_TOKEN; + + for( u_int b = 0; b < vBuffer.size(); b++ ) + sFile += vBuffer[b] + "\n"; + + CBlowfish c( m_sPassword, BF_ENCRYPT ); + sFile = c.Crypt( sFile ); + string sPath = GetPath( vChans[a]->GetName() ); + if ( !sPath.empty() ) + { + WriteFile( sPath, sFile ); + chmod( sPath.c_str(), 0600 ); + } + } + } + } + + virtual string GetDescription() + { + return ( "Stores channel buffers to disk, encrypted." ); + } + + virtual void OnModCommand( const string& sCommand ) + { + u_int iPos = sCommand.find( " " ); + string sCom, sArgs; + if ( iPos == string::npos ) + sCom = sCommand; + else + { + sCom = sCommand.substr( 0, iPos ); + sArgs = sCommand.substr( iPos + 1, string::npos ); + } + + if ( strcasecmp( sCom.c_str(), "setpass" ) == 0 ) + { + PutModule( "Password set to [" + sArgs + "]" ); + m_sPassword = CBlowfish::MD5( sArgs ); + + } else if ( strcasecmp( sCom.c_str(), "dumpbuff" ) == 0 ) + { + string sChannel = GetPath( sArgs ); + string sFile; + + if ( ( sChannel.empty() ) || ( !ReadFile( sChannel, sFile ) ) ) + { + PutModule( "Unable to find buffer for that channel" ); + return; + } + + if ( !sFile.empty() ) + { + CBlowfish c( m_sPassword, BF_DECRYPT ); + sFile = c.Crypt( sFile ); + + if ( sFile.substr( 0, strlen( CRYPT_VERIFICATION_TOKEN ) ) != CRYPT_VERIFICATION_TOKEN ) + { + // failed to decode :( + PutModule( "Unable to decode Encrypted file [" + sChannel + "]" ); + return; + } + sFile.erase( 0, strlen( CRYPT_VERIFICATION_TOKEN ) ); + + string sLine; + u_int iPos = 0; + while( ReadLine( sFile, sLine, iPos ) ) + { + CUtils::Trim( sLine ); + PutModule( "[" + sLine + "]" ); + } + } + } else if ( strcasecmp( sCom.c_str(), "save" ) == 0 ) + { + SaveBufferToDisk(); + PutModule( "Done." ); + } else + PutModule( "Unknown command [" + sCommand + "]" ); + } + + string GetPath( const string & sChannel ) + { + string sBuffer = m_pUser->GetUserName() + Lower( sChannel ); + string sRet = m_pUser->GetHomePath(); + sRet += "/.znc-savebuff-" + CBlowfish::MD5( sBuffer, true ); + return( sRet ); + } + +private: + string m_sPassword; +}; + + +void CSaveBuffJob::RunJob() +{ + CSaveBuff *p = (CSaveBuff *)m_pModule; + p->SaveBufferToDisk(); +} + +MODULEDEFS(CSaveBuff) + diff --git a/modules/schat.cc b/modules/schat.cc new file mode 100644 index 00000000..4b2595d4 --- /dev/null +++ b/modules/schat.cc @@ -0,0 +1,462 @@ +#include "main.h" +#include "User.h" +#include "Nick.h" +#include "Modules.h" +#include "Chan.h" +#include "Utils.h" +#include "Csocket.h" +#include "md5.h" +#include +#include + +/* + * Secure chat system + * Author: imaginos + * + * $Log$ + * Revision 1.1 2004/08/24 00:08:52 prozacx + * Initial revision + * + * + */ +class CSChat; + +class CRemMarkerJob : public CTimer +{ +public: + CRemMarkerJob( CModule* pModule, unsigned int uInterval, unsigned int uCycles, const string& sLabel, const string& sDescription ) + : CTimer( pModule, uInterval, uCycles, sLabel, sDescription) {} + + virtual ~CRemMarkerJob() {} + void SetNick( const string & sNick ) + { + m_sNick = sNick; + } + +protected: + virtual void RunJob(); + string m_sNick; +}; + +class CSChatSock : public Csock +{ +public: + CSChatSock( CSChat *pMod ) : Csock() + { + m_pModule = pMod; + } + CSChatSock( int itimeout = 60 ) : Csock( itimeout ) + { + m_pModule = NULL; + EnableReadLine(); + } + CSChatSock( const CS_STRING & sHost, int iPort, int iTimeout = 60 ) + : Csock( sHost, iPort, iTimeout ) + { + m_pModule = NULL; + EnableReadLine(); + } + + virtual Csock *GetSockObj( const CS_STRING & sHostname, int iPort ) + { + CSChatSock *p = new CSChatSock( sHostname, iPort ); + return( p ); + } + + virtual bool ConnectionFrom( const CS_STRING & sHost, int iPort ) + { + Close(); // close the listener after the first connection + return( true ); + } + + virtual void Connected(); + virtual bool CreatedChild( Csock *pSock ) + { + CSChatSock *p = (CSChatSock *)pSock; + p->SetModule( m_pModule ); + p->SetChatNick( m_sChatNick ); + p->SetSockName( GetSockName() + "::" + m_sChatNick ); + return( true ); + } + + virtual void Timeout(); + + void SetModule( CSChat *p ) + { + m_pModule = p; + } + void SetChatNick( const string & sNick ) + { + m_sChatNick = sNick; + } + + const string & GetChatNick() const { return( m_sChatNick ); } + + virtual void ReadLine( const CS_STRING & sLine ); + virtual void Disconnected(); + + virtual void AddLine( const string & sLine ) + { + m_vBuffer.insert( m_vBuffer.begin(), sLine ); + if ( m_vBuffer.size() > 200 ) + m_vBuffer.pop_back(); + } + + virtual void DumpBuffer() + { + for( vector::reverse_iterator it = m_vBuffer.rbegin(); it != m_vBuffer.rend(); it++ ) + ReadLine( *it ); + + m_vBuffer.clear(); + } + +private: + CSChat *m_pModule; + string m_sChatNick; + vector m_vBuffer; +}; + + + +class CSChat : public CModule +{ +public: + MODCONSTRUCTOR(CSChat) {} + virtual ~CSChat() { CleanSocks(); } + + virtual bool OnLoad( const string & sArgs ) + { + m_sPemFile = sArgs; + + if ( m_sPemFile.empty() ) + { + m_sPemFile = m_pUser->GetBinPath() + "/znc.pem"; + } + + if (!CFile::Exists(m_sPemFile)) { + PutModule("Unable to load pem file [" + m_sPemFile + "]"); + return false; + } + + return true; + } + + virtual void OnUserAttached() + { + string sName = "SCHAT::" + m_pUser->GetUserName(); + for( u_int a = 0; a < m_pManager->size(); a++ ) + { + if ( ( strncmp( (*m_pManager)[a]->GetSockName().c_str(), sName.c_str(), sName.length() ) != 0 ) || ( (*m_pManager)[a]->GetType() == CSChatSock::LISTENER ) ) + continue; + + CSChatSock *p = (CSChatSock *)(*m_pManager)[a]; + p->DumpBuffer(); + } + } + virtual void OnUserDetached() {} + + void CleanSocks() + { + string sName = "SCHAT::" + m_pUser->GetUserName(); + for( u_int a= 0; a < m_pManager->size(); a++ ) + { + if ( strncmp( (*m_pManager)[a]->GetSockName().c_str(), sName.c_str(), sName.length() ) == 0 ) + m_pManager->DelSock( a-- ); + } + } + + virtual string GetDescription() + { + return ( "Secure cross platform (:P) chat system" ); + } + + virtual bool OnUserRaw( string & sLine ) + { + if ( strncasecmp( sLine.c_str(), "schat ", 6 ) == 0 ) + { + OnModCommand( "chat " + sLine.substr( 6, string::npos ) ); + return( true ); + + } else if ( strcasecmp( sLine.c_str(), "schat" ) == 0 ) + { + PutModule( "SChat User Area ..." ); + OnModCommand( "help" ); + return( true ); + + } + + return( false ); + } + virtual void OnModCommand( const string& sCommand ) + { + u_int iPos = sCommand.find( " " ); + string sCom, sArgs; + if ( iPos == string::npos ) + sCom = sCommand; + else + { + sCom = sCommand.substr( 0, iPos ); + sArgs = sCommand.substr( iPos + 1, string::npos ); + } + + if ( ( strcasecmp( sCom.c_str(), "chat" ) == 0 ) && ( !sArgs.empty() ) ) + { + string sSockName = "SCHAT::" + m_pUser->GetUserName(); + string sNick = "(s)" + sArgs; + for( u_int a= 0; a < m_pManager->size(); a++ ) + { + if ( strncmp( (*m_pManager)[a]->GetSockName().c_str(), sSockName.c_str(), sSockName.length() ) != 0 ) + continue; + + CSChatSock *pSock = (CSChatSock *)(*m_pManager)[a]; + if ( strcasecmp( pSock->GetChatNick().c_str(), sNick.c_str() ) == 0 ) + { + PutModule( "Already Connected to [" + sArgs + "]" ); + return; + } + } + + CSChatSock *pSock = new CSChatSock; + pSock->SetCipher( "HIGH" ); + pSock->SetPemLocation( m_sPemFile ); + pSock->SetModule( this ); + pSock->SetChatNick( sNick ); + + u_short iPort = m_pManager->ListenRand( sSockName, m_pUser->GetLocalIP(), true, SOMAXCONN, pSock, 60 ); + + if ( iPort == 0 ) + { + PutModule( "Failed to start chat!" ); + return; + } + + stringstream s; + s << "PRIVMSG " << sArgs << " :\001"; + s << "DCC SCHAT chat "; + s << CUtils::GetLongIP( m_pUser->GetLocalIP() ); + s << " " << iPort << "\001"; + + PutIRC( s.str() ); + + } else if ( strcasecmp( sCom.c_str(), "list" ) == 0 ) + { + string sName = "SCHAT::" + m_pUser->GetUserName(); + CTable Table; + Table.AddColumn( "Nick" ); + Table.AddColumn( "Created" ); + Table.AddColumn( "Host" ); + Table.AddColumn( "Port" ); + Table.AddColumn( "Status" ); + Table.AddColumn( "Cipher" ); + for( u_int a= 0; a < m_pManager->size(); a++ ) + { + if ( strncmp( (*m_pManager)[a]->GetSockName().c_str(), sName.c_str(), sName.length() ) != 0 ) + continue; + + Table.AddRow(); + + CSChatSock *pSock = (CSChatSock *)(*m_pManager)[a]; + Table.SetCell( "Nick", pSock->GetChatNick() ); + unsigned long long iStartTime = pSock->GetStartTime(); + time_t iTime = iStartTime / 1000; + char *pTime = ctime( &iTime ); + if ( pTime ) + { + string sTime = pTime; + CUtils::Trim( sTime ); + Table.SetCell( "Created", sTime ); + } + + if ( pSock->GetType() != CSChatSock::LISTENER ) + { + Table.SetCell( "Status", "Established" ); + Table.SetCell( "Host", pSock->GetRemoteIP() ); + Table.SetCell( "Port", CUtils::ToString( pSock->GetRemotePort() ) ); + SSL_SESSION *pSession = pSock->GetSSLSession(); + if ( ( pSession ) && ( pSession->cipher ) && ( pSession->cipher->name ) ) + Table.SetCell( "Cipher", pSession->cipher->name ); + + } else + { + Table.SetCell( "Status", "Waiting" ); + Table.SetCell( "Port", CUtils::ToString( pSock->GetLocalPort() ) ); + } + } + if ( Table.size() ) + { + unsigned int uTableIdx = 0; + string sLine; + while ( Table.GetLine( uTableIdx++, sLine ) ) + PutModule( sLine ); + } else + PutModule( "No SDCCs currently in session" ); + + } else if ( strcasecmp( sCom.c_str(), "close" ) == 0 ) + { + string sName = "SCHAT::" + m_pUser->GetUserName(); + for( u_int a= 0; a < m_pManager->size(); a++ ) + { + if ( strncmp( (*m_pManager)[a]->GetSockName().c_str(), sName.c_str(), sName.length() ) != 0 ) + continue; + + CSChatSock *pSock = (CSChatSock *)(*m_pManager)[a]; + if ( strncasecmp( sArgs.c_str(), "(s)", 3 ) != 0 ) + sArgs = "(s)" + sArgs; + + if ( strcasecmp( sArgs.c_str(), pSock->GetChatNick().c_str() ) == 0 ) + { + pSock->Close(); + return; + } + PutModule( "No Such Chat [" + sArgs + "]" ); + } + + } else if ( strcasecmp( sCom.c_str(), "help" ) == 0 ) + { + PutModule( "Commands are: " ); + PutModule( " help - This text." ); + PutModule( " chat - Chat a nick." ); + PutModule( " list - List current chats." ); + PutModule( " close - Close a chat to a nick." ); + PutModule( " timers - Shows related timers." ); + } else if ( strcasecmp( sCom.c_str(), "timers" ) == 0 ) + ListTimers(); + else + PutModule( "Unknown command [" + sCom + "] [" + sArgs + "]" ); + } + + virtual bool OnPrivCTCP( const CNick& Nick, string& sMessage ) + { + if ( strncasecmp( sMessage.c_str(), "DCC SCHAT ", 10 ) == 0 ) + { + // chat ip port + unsigned long iIP = strtoul( CUtils::Token( sMessage, 3 ).c_str(), NULL, 10 ); + unsigned short iPort = strtoul( CUtils::Token( sMessage, 4 ).c_str(), NULL, 10 ); + + if ( ( iIP > 0 ) && ( iPort > 0 ) ) + { + pair pTmp; + pTmp.first = iIP; + pTmp.second = iPort; + m_siiWaitingChats["(s)" + Nick.GetNick()] = pTmp; + SendToUser( "(s)" + Nick.GetNick() + "!" + "(s)" + Nick.GetNick() + "@" + CUtils::GetIP( iIP ), "*** Incoming DCC SCHAT, Accept ? (yes/no)" ); + CRemMarkerJob *p = new CRemMarkerJob( this, 60, 1, "Remove (s)" + Nick.GetNick(), "Removes this nicks entry for waiting DCC." ); + p->SetNick( "(s)" + Nick.GetNick() ); + AddTimer( p ); + return( true ); + } + } + + return( false ); + } + + void AcceptSDCC( const string & sNick, u_long iIP, u_short iPort ) + { + CSChatSock *p = new CSChatSock( CUtils::GetIP( iIP ), iPort, 60 ); + p->SetModule( this ); + p->SetChatNick( sNick ); + string sSockName = "SCHAT::" + m_pUser->GetUserName() + "::" + sNick; + m_pManager->Connect( CUtils::GetIP( iIP ), iPort, sSockName, 60, true, m_pUser->GetLocalIP(), p ); + RemTimer( "Remove " + sNick ); // delete any associated timer to this nick + } + virtual bool OnUserMsg( const string& sTarget, string& sMessage ) + { + if ( strncmp( sTarget.c_str(), "(s)", 3 ) == 0 ) + { + string sSockName = "SCHAT::" + m_pUser->GetUserName() + "::" + sTarget; + CSChatSock *p = (CSChatSock *)m_pManager->FindSockByName( sSockName ); + if ( !p ) + { + map< string,pair< u_long,u_short > >::iterator it = m_siiWaitingChats.find( sTarget ); + if ( it != m_siiWaitingChats.end() ) + { + if ( strcasecmp( sMessage.c_str(), "yes" ) != 0 ) + SendToUser( sTarget + "!" + sTarget + "@" + CUtils::GetIP( it->second.first ), "Refusing to accept DCC SCHAT!" ); + else + AcceptSDCC( sTarget, it->second.first, it->second.second ); + + m_siiWaitingChats.erase( it ); + return( true ); + } + PutModule( "No such SCHAT to [" + sTarget + "]" ); + } else + p->Write( sMessage + "\n" ); + + return( true ); + } + return( false ); + } + + virtual void RemoveMarker( const string & sNick ) + { + map< string,pair< u_long,u_short > >::iterator it = m_siiWaitingChats.find( sNick ); + if ( it != m_siiWaitingChats.end() ) + m_siiWaitingChats.erase( it ); + } + + void SendToUser( const string & sFrom, const string & sText ) + { + //:*schat!znc@znc.com PRIVMSG Jim : + string sSend = ":" + sFrom + " PRIVMSG " + m_pUser->GetCurNick() + " :" + sText; + PutUser( sSend ); + } + + bool IsAttached() + { + return( m_pUser->IsUserAttached() ); + } + +private: + map< string,pair< u_long,u_short > > m_siiWaitingChats; + string m_sPemFile; +}; + + +//////////////////// methods //////////////// + +void CSChatSock::ReadLine( const CS_STRING & sLine ) +{ + if ( m_pModule ) + { + string sText = sLine; + CUtils::Trim( sText ); + if ( m_pModule->IsAttached() ) + m_pModule->SendToUser( m_sChatNick + "!" + m_sChatNick + "@" + GetRemoteIP(), sText ); + else + AddLine( sText ); + } +} + +void CSChatSock::Disconnected() +{ + if ( m_pModule ) + m_pModule->SendToUser( m_sChatNick + "!" + m_sChatNick + "@" + GetRemoteIP(), "*** Disconnected." ); +} + +void CSChatSock::Connected() +{ + SetTimeout( 0 ); + if ( m_pModule ) + m_pModule->SendToUser( m_sChatNick + "!" + m_sChatNick + "@" + GetRemoteIP(), "*** Connected." ); +} + +void CSChatSock::Timeout() +{ + if ( m_pModule ) + { + if ( GetType() == LISTENER ) + m_pModule->PutModule( "Timeout while waiting for [" + m_sChatNick + "]" ); + else + m_pModule->SendToUser( m_sChatNick + "!" + m_sChatNick + "@" + GetRemoteIP(), "*** Connection Timed out." ); + } +} + +void CRemMarkerJob::RunJob() +{ + CSChat *p = (CSChat *)m_pModule; + p->RemoveMarker( m_sNick ); + + // store buffer +} +MODULEDEFS(CSChat) + diff --git a/modules/shell.cpp b/modules/shell.cpp new file mode 100644 index 00000000..5efa1bc5 --- /dev/null +++ b/modules/shell.cpp @@ -0,0 +1,220 @@ +#ifndef _NO_CSOCKET_NS +#define _NO_CSOCKET_NS +#endif + +#include "Csocket.h" +#include "User.h" +#include "Nick.h" +#include "Modules.h" +#include "Chan.h" +#include "Utils.h" +#include + +// Forward Declaration +class CShellMod; + +class CExecSock : public Csock { +public: + CExecSock(CShellMod* pShellMod, const string& sExec) : Csock() { + EnableReadLine(); + m_pParent = pShellMod; + int iReadFD, iWriteFD; + m_iPid = popen2(iReadFD, iWriteFD, sExec); + ConnectFD(iReadFD, iWriteFD, "0.0.0.0:0"); + } + + virtual ~CExecSock() { + close2(m_iPid, GetRSock(), GetWSock()); + SetRSock( -1 ); + SetWSock( -1 ); + } + + // These next two function's bodies are at the bottom of the file since they reference CShellMod + virtual void ReadLine(const string& sData); + virtual void Disconnected(); + + CShellMod* m_pParent; + int m_iPid; + + int popen2(int & iReadFD, int & iWriteFD, const string & sCommand) { + int rpipes[2] = { -1, -1 }; + int wpipes[2] = { -1, -1 }; + iReadFD = -1; + iWriteFD = -1; + + pipe(rpipes); + pipe(wpipes); + + int iPid = fork(); + + if (iPid == -1) { + return -1; + } + + if (iPid == 0) { + close(wpipes[1]); + close(rpipes[0]); + dup2(wpipes[0], 0); + dup2(rpipes[1], 1); + dup2(rpipes[1], 2); + close(wpipes[0]); + close(rpipes[1]); + system( sCommand.c_str() ); + exit(0); + } + + close(wpipes[0]); + close(rpipes[1]); + + iWriteFD = wpipes[1]; + iReadFD = rpipes[0]; + + return iPid; + } + + void close2(int iPid, int iReadFD, int iWriteFD) { + close( iReadFD ); + close( iWriteFD ); + u_int iNow = time( NULL ); + while( waitpid( iPid, NULL, WNOHANG ) == 0 ) + { + if ( ( time( NULL ) - iNow ) > 5 ) + break; // giveup + usleep( 100 ); + } + return; + } +}; + +class CShellMod : public CModule { +public: + MODCONSTRUCTOR(CShellMod) { + m_sPath = pUser->GetHomePath(); + } + + virtual ~CShellMod() { + vector vSocks = m_pManager->FindSocksByName("SHELL"); + + for (unsigned int a = 0; a < vSocks.size(); a++) { + m_pManager->DelSockByAddr(vSocks[a]); + } + } + + virtual string GetDescription() { + return "Gives shell access."; + } + + virtual void OnModCommand(const string& sCommand) { + if ((strcasecmp(sCommand.c_str(), "cd") == 0) || (strncasecmp(sCommand.c_str(), "cd ", 3) == 0)) { + string sPath = CUtils::ChangeDir(m_sPath, ((sCommand.length() == 2) ? m_pUser->GetHomePath() : sCommand.substr(3)), m_pUser->GetHomePath()); + CFile Dir(sPath); + + if (Dir.IsDir()) { + m_sPath = sPath; + } else if (Dir.Exists()) { + PutShell("cd: not a directory [" + sPath + "]"); + } else { + PutShell("cd: no such directory [" + sPath + "]"); + } + + PutShell("znc$"); + } else if (strcasecmp(CUtils::Token(sCommand, 0).c_str(), "SEND") == 0) { + string sToNick = CUtils::Token(sCommand, 1); + string sFile = CUtils::Token(sCommand, 2); + + if ((sToNick.empty()) || (sFile.empty())) { + PutShell("usage: Send "); + } else { + sFile = CUtils::ChangeDir(m_sPath, sFile, m_pUser->GetHomePath()); + + if (!CFile::Exists(sFile)) { + PutShell("get: no such file [" + sFile + "]"); + } else if (!CFile::IsReg(sFile)) { + PutShell("get: not a file [" + sFile + "]"); + } else { + m_pUser->SendFile(sToNick, sFile, GetModName()); + } + } + } else if (strcasecmp(CUtils::Token(sCommand, 0).c_str(), "GET") == 0) { + string sFile = CUtils::Token(sCommand, 1); + + if (sFile.empty()) { + PutShell("usage: Get "); + } else { + sFile = CUtils::ChangeDir(m_sPath, sFile, m_pUser->GetHomePath()); + + if (!CFile::Exists(sFile)) { + PutShell("get: no such file [" + sFile + "]"); + } else if (!CFile::IsReg(sFile)) { + PutShell("get: not a file [" + sFile + "]"); + } else { + m_pUser->SendFile(m_pUser->GetCurNick(), sFile, GetModName()); + } + } + } else { + RunCommand(sCommand); + } + } + + virtual bool OnStatusCommand(const string& sCommand) { + if (strcasecmp(sCommand.c_str(), "SHELL") == 0) { + PutShell("-- ZNC Shell Service --"); + return true; + } + + return false; + } + + virtual bool OnDCCUserSend(const CNick& RemoteNick, unsigned long uLongIP, unsigned short uPort, const string& sFile, unsigned long uFileSize) { + if (strcasecmp(RemoteNick.GetNick().c_str(), string(GetModNick()).c_str()) == 0) { + string sLocalFile = CUtils::ChangeDir(m_sPath, sFile, m_pUser->GetHomePath()); + + m_pUser->GetFile(m_pUser->GetCurNick(), CUtils::GetIP(uLongIP), uPort, sLocalFile, uFileSize, GetModName()); + + return true; + } + + return false; + } + + void PutShell(const string& sLine) { + string sPath = m_sPath; + + unsigned int a = sPath.find(' '); + while (a != string::npos) { + sPath.replace(a, 1, "_"); + a = sPath.find(' '); + } + + PutModule(sLine, m_pUser->GetCurNick(), sPath); + } + + void RunCommand(const string& sCommand) { + m_pManager->AddSock((Csock*) new CExecSock(this, "cd " + m_sPath + " && " + sCommand), "SHELL"); + } +private: + string m_sPath; +}; + +void CExecSock::ReadLine(const string& sData) { + string sLine = sData; + + while ((sLine.length()) && (sLine[sLine.length() -1] == '\r') || (sLine[sLine.length() -1] == '\n')) { + sLine = sLine.substr(0, sLine.length() -1); + } + + unsigned int a = sLine.find('\t'); + while (a != string::npos) { + sLine.replace(a, 1, " "); + a = sLine.find('\t'); + } + + m_pParent->PutShell(sLine); +} + +void CExecSock::Disconnected() { + m_pParent->PutShell("znc$"); +} + +MODULEDEFS(CShellMod) + diff --git a/znc.conf b/znc.conf new file mode 100644 index 00000000..d20cc387 --- /dev/null +++ b/znc.conf @@ -0,0 +1,95 @@ +/* This is znc's config file */ +/* C style comments are supported, but may only be preceded by whitespace. */ +/* perl style #comments are also supported, and also must be preceded only by whitespace. */ +/* the ending of multiline comments (star slash) must be at the end of a line. */ + + +// This is the port that znc will listen on +// If the port is prepended with a '+' then znc listens using ssl. Note: znc must be compiled with ssl enabled. +ListenPort = 12345 + +// If you define the ISpoofFile, znc will write the ident of the user trying to connect to the file. Very useful if your box supports oidentd. +#ISpoofFile = /tmp/ispoof + +// This is the pid file that is needed if you want to crontab the zncchk shell script +PidFile = znc.pid + +// This is the prefix for the status window. Some clients or scripts may require you to change it, default is *status +StatusPrefix = * + +// User definition + + + // You can override the global StatusPrefix on a per user basis. Using - is a good alternative in mIRC but it breaks BitchX + #StatusPrefix = - + + // Password used to connect. + Pass = pass + + // You can use an md5 hash by supplying a - after the hash (with a space between) + // To generate a hashed password, use ./znc --makepass + // This next line would be just like doing "Pass = pass" like above, except your pass is not exposed simply by reading the config + #Pass = 1a1dc91c907325c69271ddf0c944bc72 - + + Nick = zncuser + AltNick = zncus3r + Ident = znc + RealName = Got ZNC? + #VHost = uncomment.and.put.your.vhost.here.com + ChanModes = +stn + KeepNick = true + + // You may use multiple Allow lines to restrict access to this user. All connections are denied by default unless they match an ip below. + // Note: IPs only! + Allow = * + #Allow = 24.24.24.* + #Allow = 192.168.1.* + + // Uncomment this next line if you want znc to use the longip that your client sends it when establishing a dcc connection + // By default znc will use the ip that you connect to it to contact your end of a bounced dcc + #DCCLookupMethod = Client + + // You can write modules and dynamically load them at runtime + + // Uncomment the next line to deny this user access to (un)loading modules. Only the modules you load for them in this config will be available to them + #DenyLoadMod = true + + // The sample module does random things, it is meant to be a sample for module authors to learn from + #LoadModule = sample + + // The shell module gives you shell access to the system in a query window + #LoadModule = shell + + // The savebuff module is a module that saves your (encrypted) channel buffers to disk. It is best used when KeepBuffer = true + #LoadModule = savebuff + + // Add the servers that you wish znc to use when connecting to IRC. [[+]port [pass]] + // If the port is prepended with a '+' then znc connects to this server with ssl. Note: znc must be compiled with ssl enabled. + Server = irc.nac.net 6667 + Server = irc.efnet.pl 6667 + #Server = irc.someserver.com +8000 serverpass + + // Add channels that you want znc to join on start. + + Key = znc + Modes = +sn-t + + // This is the playback buffer size (in LINES) - WARNING: setting this too high could cause you to get a lot of text when you attach + Buffer = 50 + + // This will play the whole buffer back to you each time you connect. It will also buffer channels while you are attached + #KeepBuffer = true + + + +// Multiple users are supported, this one is commented out +/* + Pass = somepass + Nick = user2 + Ident = user2 + RealName = Second User + Allow = * + + Server = irc.efnet.pl 6667 +*/ + diff --git a/znc.cpp b/znc.cpp new file mode 100644 index 00000000..333d4a40 --- /dev/null +++ b/znc.cpp @@ -0,0 +1,471 @@ +#include "znc.h" +#include "User.h" +#include "Server.h" +#include "UserSock.h" +#include "IRCSock.h" +#include "Utils.h" + +#include +#include + +#ifdef _MODULES +#include "Modules.h" +#endif + +CZNC::CZNC() { + m_uListenPort = 0; + m_bISpoofLocked = false; +} + +CZNC::~CZNC() { + m_Manager.Cleanup(); + DeleteUsers(); +} + +bool CZNC::OnBoot() { + for (map::iterator it = m_msUsers.begin(); it != m_msUsers.end(); it++) { + if (!it->second->OnBoot()) { + return false; + } + } + + return true; +} + +int CZNC::Loop() { + m_Manager.SetSelectTimeout(10000); + m_itUserIter = m_msUsers.begin(); + + while (true) { + m_Manager.Loop(); + + if (m_bISpoofLocked) { + continue; + } + + if (m_itUserIter == m_msUsers.end()) { + m_itUserIter = m_msUsers.begin(); + } + + string sSockName = "IRC::" + m_itUserIter->first; + CUser* pUser = m_itUserIter->second; + + m_itUserIter++; + + CIRCSock* pIRCSock = (CIRCSock*) m_Manager.FindSockByName(sSockName); + + if (!pIRCSock) { + CServer* pServer = pUser->GetNextServer(); + + if (!pServer) { + continue; + } + + if (!m_sISpoofFile.empty()) { + CFile File(m_sISpoofFile); + + if (File.Open(O_RDONLY)) { + char buf[1024]; + memset((char*) buf, 0, 1024); + File.Read(buf, 1023); + File.Close(); + m_sOrigISpoof = buf; + } + + if (File.Open(O_WRONLY | O_TRUNC | O_CREAT)) { + File.Write(pUser->GetIdent() + "\r\n"); + File.Close(); + } + + m_bISpoofLocked = true; + } + + DEBUG_ONLY( cout << "User [" << pUser->GetUserName() << "] is connecting to [" << pServer->GetName() << ":" << pServer->GetPort() << "] ..." << endl); + CUserSock* pUserSock = pUser->GetUserSock(); + + if (pUserSock) { + pUserSock->PutStatus("Attempting to connect to [" + pServer->GetName() + ":" + CUtils::ToString(pServer->GetPort()) + "] ..."); + } + + pIRCSock = new CIRCSock(this, pUser); + pIRCSock->SetPass(pServer->GetPass()); + + bool bSSL = false; +#ifdef HAVE_LIBSSL + if (pServer->IsSSL()) { + bSSL = true; + pIRCSock->SetPemLocation(GetBinPath() + "/znc.pem"); + } +#endif + if (!m_Manager.Connect(pServer->GetName(), pServer->GetPort(), sSockName, 20, bSSL, pUser->GetVHost(), pIRCSock)) { + ReleaseISpoof(); + + if (pUserSock) { + pUserSock->PutStatus("Unable to connect. (Bad host?)"); + } + } + } + } + + return 0; +} + +void CZNC::ReleaseISpoof() { + if (!m_sISpoofFile.empty()) { + CFile File(m_sISpoofFile); + + if (File.Open(O_WRONLY | O_TRUNC | O_CREAT)) { + File.Write(m_sOrigISpoof); + File.Close(); + } + + m_sOrigISpoof = ""; + } + + m_bISpoofLocked = false; +} + +bool CZNC::WritePidFile(int iPid) { + if (!m_sPidFile.empty()) { + CFile File(m_sPidFile); + + if (File.Open(O_WRONLY | O_TRUNC | O_CREAT)) { + File.Write(CUtils::ToString(iPid) + "\n"); + File.Close(); + return true; + } + } + + return false; +} + +void CZNC::DeleteUsers() { + for (map::iterator a = m_msUsers.begin(); a != m_msUsers.end(); a++) { + delete a->second; + } + + m_msUsers.clear(); + m_itUserIter = m_msUsers.end(); +} + +CUser* CZNC::GetUser(const string& sUser) { + // Todo: make this case insensitive + map::iterator it = m_msUsers.find(sUser); + return (it == m_msUsers.end()) ? NULL : it->second; +} + +Csock* CZNC::FindSockByName(const string& sSockName) { + return m_Manager.FindSockByName(sSockName); +} + +bool CZNC::Listen() { + if (!m_uListenPort) { + return false; + } + + CUserSock* pUserSock = new CUserSock; + pUserSock->SetZNC(this); + + bool bSSL = false; +#ifdef HAVE_LIBSSL + if (IsSSL()) { + bSSL = true; + pUserSock->SetPemLocation(GetBinPath() + "/znc.pem"); + } +#endif + return m_Manager.ListenAll(m_uListenPort, "_LISTENER", bSSL, SOMAXCONN, pUserSock); +} + +bool CZNC::IsHostAllowed(const string& sHostMask) { + for (map::iterator a = m_msUsers.begin(); a != m_msUsers.end(); a++) { + if (a->second->IsHostAllowed(sHostMask)) { + return true; + } + } + + return false; +} + +void CZNC::InitDirs(const string& sArgvPath) { + char buf[PATH_MAX]; + getcwd(buf, PATH_MAX); + + // If the bin was not ran from the current directory, we need to add that dir onto our cwd + unsigned int uPos = sArgvPath.rfind('/'); + m_sBinPath = (uPos == string::npos) ? string(buf) : CUtils::ChangeDir(buf, sArgvPath.substr(0, uPos), ""); + + // Try to set the user's home dir, default to binpath on failure + struct passwd* pUserInfo = getpwuid(getuid()); + + if (pUserInfo) { + m_sHomePath = pUserInfo->pw_dir; + } + + if (m_sHomePath.empty()) { + m_sHomePath = m_sBinPath; + } + + // Other dirs that we use + m_sDLPath = m_sBinPath + "/downloads"; + m_sModPath = m_sBinPath + "/modules"; +} + +bool CZNC::ParseConfig(const string& sConfigFile) { + string sStatusPrefix; + + string sFilePath = sConfigFile; + if (CUtils::Left(sConfigFile, 1) != "/") { + sFilePath = m_sBinPath + "/" + sFilePath; + } + + CFile File(sFilePath); + + if (!File.Open(O_RDONLY)) { + cerr << "Could not open config [" << sFilePath << "]" << endl; + return false; + } + + string sLine; + bool bCommented = false; // support for /**/ style comments + CUser* pUser = NULL; // Used to keep track of which user block we are in + CChan* pChan = NULL; // Used to keep track of which chan block we are in + + while (File.ReadLine(sLine)) { + while ((CUtils::Right(sLine, 1) == "\r") || (CUtils::Right(sLine, 1) == "\n")) { + CUtils::Trim(sLine); + } + + if ((sLine.empty()) || (sLine[0] == '#') || (CUtils::Left(sLine, 2) == "//")) { + continue; + } + + if (CUtils::Left(sLine, 2) == "/*") { + if (CUtils::Right(sLine, 2) != "*/") { + bCommented = true; + } + + continue; + } + + if (bCommented) { + if (CUtils::Right(sLine, 2) == "*/") { + bCommented = false; + } + + continue; + } + + if ((CUtils::Left(sLine, 1) == "<") && (CUtils::Right(sLine, 1) == ">")) { + CUtils::LeftChomp(sLine); + CUtils::RightChomp(sLine); + CUtils::Trim(sLine); + + string sTag = sLine.substr(0, sLine.find_first_of(" \t\r\n")); + string sValue = (sTag.size() < sLine.size()) ? sLine.substr(sTag.size() +1) : ""; + + CUtils::Trim(sTag); + CUtils::Trim(sValue); + + if (CUtils::Left(sLine, 1) == "/") { + string sTag = sLine.substr(1); + + if (pUser) { + if (pChan) { + if (strcasecmp(sTag.c_str(), "Chan") == 0) { + pUser->AddChan(pChan); + pChan = NULL; + continue; + } + } else if (strcasecmp(sTag.c_str(), "User") == 0) { + pUser = NULL; + continue; + } + } + } else if (strcasecmp(sTag.c_str(), "User") == 0) { + if (pUser) { + cerr << "You may not nest tags inside of other tags." << endl; + return false; + } + + if (sValue.empty()) { + cerr << "You must supply a username in the tag." << endl; + return false; + } + + if (m_msUsers.find(sValue) != m_msUsers.end()) { + cerr << "User [" << sValue << "] defined more than once." << endl; + return false; + } + + pUser = new CUser(sValue, this); + if (!sStatusPrefix.empty()) { + if (!pUser->SetStatusPrefix(sStatusPrefix)) { + cerr << "Invalid StatusPrefix [" + sStatusPrefix + "] Must be 1-5 chars, no spaces." << endl; + } + } + m_msUsers[sValue] = pUser; + continue; + } else if (strcasecmp(sTag.c_str(), "Chan") == 0) { + if (!pUser) { + cerr << " tags must be nested inside of a tag." << endl; + return false; + } + + if (pChan) { + cerr << "You may not nest tags inside of other tags." << endl; + return false; + } + + pChan = new CChan(sValue, pUser); + continue; + } + } + + // If we have a regular line, figure out where it goes + string sName = CUtils::Token(sLine, 0, false, '='); + string sValue = CUtils::Token(sLine, 1, true, '='); + CUtils::Trim(sName); + CUtils::Trim(sValue); + + if ((!sName.empty()) && (!sValue.empty())) { + if (pUser) { + if (pChan) { + if (strcasecmp(sName.c_str(), "Buffer") == 0) { + pChan->SetBufferCount(strtoul(sValue.c_str(), NULL, 10)); + continue; + } else if (strcasecmp(sName.c_str(), "KeepBuffer") == 0) { + pChan->SetKeepBuffer((strcasecmp(sValue.c_str(), "true") == 0)); + continue; + } else if (strcasecmp(sName.c_str(), "Key") == 0) { + pChan->SetKey(sValue); + continue; + } else if (strcasecmp(sName.c_str(), "Modes") == 0) { + pChan->SetDefaultModes(sValue); + continue; + } + } else { + if (strcasecmp(sName.c_str(), "Nick") == 0) { + pUser->SetNick(sValue); + continue; + } else if (strcasecmp(sName.c_str(), "AltNick") == 0) { + pUser->SetAltNick(sValue); + continue; + } else if (strcasecmp(sName.c_str(), "Pass") == 0) { + if (CUtils::Right(sValue, 1) == "-") { + CUtils::RightChomp(sValue); + CUtils::Trim(sValue); + pUser->SetPass(sValue, true); + } else { + pUser->SetPass(sValue, false); + } + + continue; + } else if (strcasecmp(sName.c_str(), "Ident") == 0) { + pUser->SetIdent(sValue); + continue; + } else if (strcasecmp(sName.c_str(), "DenyLoadMod") == 0) { + pUser->SetDenyLoadMod((strcasecmp(sValue.c_str(), "TRUE") == 0)); + continue; + } else if (strcasecmp(sName.c_str(), "StatusPrefix") == 0) { + if (!pUser->SetStatusPrefix(sValue)) { + cerr << "Invalid StatusPrefix [" + sValue + "] Must be 1-5 chars, no spaces." << endl; + } + continue; + } else if (strcasecmp(sName.c_str(), "DCCLookupMethod") == 0) { + pUser->SetUseClientIP((strcasecmp(sValue.c_str(), "Client") == 0)); + continue; + } else if (strcasecmp(sName.c_str(), "RealName") == 0) { + pUser->SetRealName(sValue); + continue; + } else if (strcasecmp(sName.c_str(), "KeepNick") == 0) { + pUser->SetKeepNick((strcasecmp(sValue.c_str(), "true") == 0)); + continue; + } else if (strcasecmp(sName.c_str(), "ChanModes") == 0) { + pUser->SetDefaultChanModes(sValue); + continue; + } else if (strcasecmp(sName.c_str(), "VHost") == 0) { + pUser->SetVHost(sValue); + continue; + } else if (strcasecmp(sName.c_str(), "Allow") == 0) { + pUser->AddAllowedHost(sValue); + continue; + } else if (strcasecmp(sName.c_str(), "Server") == 0) { + if (!pUser->AddServer(sValue)) { + cerr << "Unable to add server [" << sValue << "]" << endl; + } + continue; + } else if (strcasecmp(sName.c_str(), "Chan") == 0) { + pUser->AddChan(sValue); + continue; + } else if (strcasecmp(sName.c_str(), "LoadModule") == 0) { +#ifdef _MODULES + string sModRet; + string sModName = CUtils::Token(sValue, 0); + string sArgs = CUtils::Token(sValue, 1, true); + + pUser->GetModules().LoadModule(sModName, sArgs, pUser, m_sBinPath, sModRet); + cout << sModRet << endl; +#else + cerr << "Unable to load [" << sValue << "] Modules are not enabled." << endl; +#endif + continue; + } + } + } else { + if (strcasecmp(sName.c_str(), "ListenPort") == 0) { + m_bSSL = false; + string sPort = sValue; + if (CUtils::Left(sPort, 1) == "+") { + CUtils::LeftChomp(sPort); + m_bSSL = true; + } + + m_uListenPort = strtol(sPort.c_str(), NULL, 10); + +#ifndef HAVE_LIBSSL + if (m_bSSL) { + cerr << "SSL is not enabled, could not bind to ssl port [" << m_uListenPort << "]" << endl; + return false; + } +#endif + + if ((m_bSSL) && (!CFile::Exists(GetBinPath() + "/znc.pem"))) { + cerr << "Unable to locate pem file: [" << GetBinPath() << "/znc.pem]" << endl; + return false; + } + + if (!Listen()) { + cerr << "Could not bind to port [" << m_uListenPort << "]" << endl; + return false; + } + + continue; + } else if (strcasecmp(sName.c_str(), "ISpoofFile") == 0) { + m_sISpoofFile = sValue; + continue; + } else if (strcasecmp(sName.c_str(), "PidFile") == 0) { + m_sPidFile = sValue; + continue; + } else if (strcasecmp(sName.c_str(), "StatusPrefix") == 0) { + sStatusPrefix = sValue; + continue; + } + } + } + + cerr << "Unhandled line in config: [" << sLine << "]" << endl; + } + + if (pChan) { + delete pChan; + } + + File.Close(); + + if (!m_msUsers.size()) { + cerr << "You must define at least one user in your config." << endl; + return false; + } + + return true; +} diff --git a/znc.h b/znc.h new file mode 100644 index 00000000..71d9c367 --- /dev/null +++ b/znc.h @@ -0,0 +1,68 @@ +#ifndef _ZNC_H +#define _ZNC_H + +#include "main.h" +#include +using std::map; + +class CUser; +class CUserSock; + +class CZNC { +public: + CZNC(); + virtual ~CZNC(); + + void DeleteUsers(); + int Loop(); + void ReleaseISpoof(); + bool WritePidFile(int iPid); + CUser* GetUser(const string& sUser); + Csock* FindSockByName(const string& sSockName); + bool Listen(); + bool ParseConfig(const string& sConfigFile); + bool IsHostAllowed(const string& sHostMask); + void InitDirs(const string& sArgvPath); + bool OnBoot(); + + // Getters + TSocketManager& GetManager() { return m_Manager; } + unsigned int GetListenPort() const { return m_uListenPort; } + const string& GetBinPath() const { return m_sBinPath; } + const string& GetDLPath() const { return m_sDLPath; } + const string& GetModPath() const { return m_sModPath; } + const string& GetHomePath() const { return m_sHomePath; } + bool IsSSL() const { +#ifdef HAVE_LIBSSL + return m_bSSL; +#endif + return false; + } + // !Getters + + // Static allocator + static CZNC* New() { + static CZNC* pZNC = new CZNC; + return pZNC; + } + +private: +protected: + unsigned short m_uListenPort; + map m_msUsers; + TSocketManager m_Manager; + + string m_sBinPath; + string m_sDLPath; + string m_sModPath; + string m_sHomePath; + + string m_sISpoofFile; + string m_sOrigISpoof; + string m_sPidFile; + bool m_bISpoofLocked; + bool m_bSSL; + map::iterator m_itUserIter; // This needs to be reset to m_msUsers.begin() if anything is added or removed to the map +}; + +#endif // !_ZNC_H diff --git a/zncchk b/zncchk new file mode 100755 index 00000000..93ccc4be --- /dev/null +++ b/zncchk @@ -0,0 +1,25 @@ +#!/bin/sh +dir="." +cmd="znc" +pidfile="znc.pid" + +PATH=.:$PATH +export PATH + +cd $dir + +if test -s $pidfile; then + pid=`cat $pidfile` + + if `kill -0 $pid >/dev/null 2>&1`; then + exit 0 + fi + + rm -f $pidfile +fi + +echo "" +echo "Reloading..." +echo "" +$cmd +exit 0