Files
znc/modules/schat.cpp
psychon 08088be7d6 Add traffic tracking support to CSocket
Now every module that uses CSocket automatically gets its generated traffic
counted. Those which use Csock directly should be shot and burried anyway ;)

This adds CModule::IsGlobal().


git-svn-id: https://znc.svn.sourceforge.net/svnroot/znc/trunk@1283 726aef4b-f618-498e-8847-2d620e286838
2008-12-06 19:56:38 +00:00

494 lines
13 KiB
C++

/*
* Copyright (C) 2004-2008 See the AUTHORS file for details.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published
* by the Free Software Foundation.
*
* Secure chat system
* Author: imaginos <imaginos@imaginos.net>
*/
#define REQUIRESSL
#include "User.h"
#include "znc.h"
#include <sstream>
using std::pair;
using std::stringstream;
class CSChat;
class CRemMarkerJob : public CTimer
{
public:
CRemMarkerJob(CModule* pModule, unsigned int uInterval, unsigned int uCycles, const CString& sLabel,
const CString& sDescription)
: CTimer(pModule, uInterval, uCycles, sLabel, sDescription) {}
virtual ~CRemMarkerJob() {}
void SetNick(const CString & sNick)
{
m_sNick = sNick;
}
protected:
virtual void RunJob();
CString 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, u_short iPort, int iTimeout = 60)
: Csock(sHost, iPort, iTimeout)
{
m_pModule = NULL;
EnableReadLine();
}
virtual Csock *GetSockObj(const CS_STRING & sHostname, u_short iPort)
{
CSChatSock *p = new CSChatSock(sHostname, iPort);
p->SetModule(m_pModule);
p->SetChatNick(m_sChatNick);
p->SetSockName(GetSockName() + "::" + m_sChatNick);
return(p);
}
virtual bool ConnectionFrom(const CS_STRING & sHost, u_short iPort)
{
Close(); // close the listener after the first connection
return(true);
}
virtual void Connected();
virtual void Timeout();
void SetModule(CSChat *p)
{
m_pModule = p;
}
void SetChatNick(const CString & sNick)
{
m_sChatNick = sNick;
}
const CString & GetChatNick() const { return(m_sChatNick); }
virtual void ReadLine(const CS_STRING & sLine);
virtual void Disconnected();
virtual void AddLine(const CString & sLine)
{
m_vBuffer.insert(m_vBuffer.begin(), sLine);
if (m_vBuffer.size() > 200)
m_vBuffer.pop_back();
}
virtual void DumpBuffer()
{
if (m_vBuffer.empty()) {
// Always show a message to the user, so he knows
// this schat still exists.
ReadLine("*** Reattached.");
} else {
// Buffer playback
vector<CS_STRING>::reverse_iterator it = m_vBuffer.rbegin();
for (; it != m_vBuffer.rend(); it++)
ReadLine(*it);
m_vBuffer.clear();
}
}
private:
CSChat *m_pModule;
CString m_sChatNick;
vector<CS_STRING> m_vBuffer;
};
class CSChat : public CModule
{
public:
MODCONSTRUCTOR(CSChat) {}
virtual ~CSChat() { CleanSocks(); }
virtual bool OnLoad(const CString & sArgs, CString & sMessage)
{
m_sPemFile = sArgs;
if (m_sPemFile.empty()) {
m_sPemFile = CZNC::Get().GetPemLocation();
}
if (!CFile::Exists(m_sPemFile)) {
sMessage = "Unable to load pem file [" + m_sPemFile + "]";
return false;
}
return true;
}
virtual void OnClientLogin()
{
CString sName = "SCHAT::" + m_pUser->GetUserName();
for (u_int a = 0; a < m_pManager->size(); a++) {
if ((*m_pManager)[a]->GetSockName() != sName ||
(*m_pManager)[a]->GetType() == CSChatSock::LISTENER)
continue;
CSChatSock *p = (CSChatSock *)(*m_pManager)[a];
p->DumpBuffer();
}
}
void CleanSocks()
{
CString sName = "SCHAT::" + m_pUser->GetUserName();
for (u_int a= 0; a < m_pManager->size(); a++) {
if ((*m_pManager)[a]->GetSockName() == sName)
m_pManager->DelSock(a--);
}
}
virtual EModRet OnUserRaw(CString & sLine)
{
if (sLine.Equals("schat ", false, 6)) {
OnModCommand("chat " + sLine.substr(6));
return(HALT);
} else if (sLine.Equals("schat")) {
PutModule("SChat User Area ...");
OnModCommand("help");
return(HALT);
}
return(CONTINUE);
}
virtual void OnModCommand(const CString& sCommand)
{
CString sCom = sCommand.Token(0);
CString sArgs = sCommand.Token(1, true);
if (sCom.Equals("chat") && !sArgs.empty()) {
CString sSockName = "SCHAT::" + m_pUser->GetUserName();
CString sNick = "(s)" + sArgs;
for (u_int a= 0; a < m_pManager->size(); a++) {
if ((*m_pManager)[a]->GetSockName() != sSockName)
continue;
CSChatSock *pSock = (CSChatSock *)(*m_pManager)[a];
if (pSock->GetChatNick().Equals(sNick)) {
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 (sCom.Equals("list")) {
CString 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 ((*m_pManager)[a]->GetSockName() != sName)
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) {
CString sTime = pTime;
sTime.Trim();
Table.SetCell("Created", sTime);
}
if (pSock->GetType() != CSChatSock::LISTENER) {
Table.SetCell("Status", "Established");
Table.SetCell("Host", pSock->GetRemoteIP());
Table.SetCell("Port", CString(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", CString(pSock->GetLocalPort()));
}
}
if (Table.size()) {
PutModule(Table);
} else
PutModule("No SDCCs currently in session");
} else if (sCom.Equals("close")) {
CString sName = "SCHAT::" + m_pUser->GetUserName();
for (u_int a = 0; a < m_pManager->size(); a++) {
if ((*m_pManager)[a]->GetSockName() != sName)
continue;
CSChatSock *pSock = (CSChatSock *)(*m_pManager)[a];
if (!sArgs.Equals("(s)", false, 3))
sArgs = "(s)" + sArgs;
if (sArgs.Equals(pSock->GetChatNick())) {
pSock->Close();
return;
}
}
PutModule("No Such Chat [" + sArgs + "]");
} else if (sCom.Equals("showsocks") && m_pUser->IsAdmin()) {
CTable Table;
Table.AddColumn("SockName");
Table.AddColumn("Created");
Table.AddColumn("LocalIP:Port");
Table.AddColumn("RemoteIP:Port");
Table.AddColumn("Type");
Table.AddColumn("Cipher");
for (u_int a = 0; a < m_pManager->size(); a++) {
Table.AddRow();
Csock *pSock = (*m_pManager)[a];
Table.SetCell("SockName", pSock->GetSockName());
unsigned long long iStartTime = pSock->GetStartTime();
time_t iTime = iStartTime / 1000;
char *pTime = ctime(&iTime);
if (pTime) {
CString sTime = pTime;
sTime.Trim();
Table.SetCell("Created", sTime);
}
if (pSock->GetType() != Csock::LISTENER) {
if (pSock->GetType() == Csock::OUTBOUND)
Table.SetCell("Type", "Outbound");
else
Table.SetCell("Type", "Inbound");
Table.SetCell("LocalIP:Port", pSock->GetLocalIP() + ":" +
CString(pSock->GetLocalPort()));
Table.SetCell("RemoteIP:Port", pSock->GetRemoteIP() + ":" +
CString(pSock->GetRemotePort()));
SSL_SESSION *pSession = pSock->GetSSLSession();
if (pSession && pSession->cipher && pSession->cipher->name)
Table.SetCell("Cipher", pSession->cipher->name);
else
Table.SetCell("Cipher", "None");
} else {
Table.SetCell("Type", "Listener");
Table.SetCell("LocalIP:Port", pSock->GetLocalIP() +
":" + CString(pSock->GetLocalPort()));
Table.SetCell("RemoteIP:Port", "0.0.0.0:0");
}
}
if (Table.size())
PutModule(Table);
else
PutModule("Error Finding Sockets");
} else if (sCom.Equals("help")) {
PutModule("Commands are:");
PutModule(" help - This text.");
PutModule(" chat <nick> - Chat a nick.");
PutModule(" list - List current chats.");
PutModule(" close <nick> - Close a chat to a nick.");
PutModule(" timers - Shows related timers.");
if (m_pUser->IsAdmin()) {
PutModule(" showsocks - Shows all socket connections.");
}
} else if (sCom.Equals("timers"))
ListTimers();
else
PutModule("Unknown command [" + sCom + "] [" + sArgs + "]");
}
virtual EModRet OnPrivCTCP(CNick& Nick, CString& sMessage)
{
if (sMessage.Equals("DCC SCHAT ", false, 10)) {
// chat ip port
unsigned long iIP = sMessage.Token(3).ToULong();
unsigned short iPort = sMessage.Token(4).ToUShort();
if (iIP > 0 && iPort > 0) {
pair<u_long, u_short> pTmp;
CString sMask;
pTmp.first = iIP;
pTmp.second = iPort;
sMask = "(s)" + Nick.GetNick() + "!" + "(s)" +
Nick.GetNick() + "@" + CUtils::GetIP(iIP);
m_siiWaitingChats["(s)" + Nick.GetNick()] = pTmp;
SendToUser(sMask, "*** 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(HALT);
}
}
return(CONTINUE);
}
void AcceptSDCC(const CString & sNick, u_long iIP, u_short iPort)
{
CSChatSock *p = new CSChatSock(CUtils::GetIP(iIP), iPort, 60);
p->SetModule(this);
p->SetChatNick(sNick);
CString 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 EModRet OnUserMsg(CString& sTarget, CString& sMessage)
{
if (sTarget.Left(3) == "(s)") {
CString sSockName = "SCHAT::" + m_pUser->GetUserName() + "::" + sTarget;
CSChatSock *p = (CSChatSock *)m_pManager->FindSockByName(sSockName);
if (!p) {
map< CString,pair< u_long,u_short > >::iterator it;
it = m_siiWaitingChats.find(sTarget);
if (it != m_siiWaitingChats.end()) {
if (!sMessage.Equals("yes"))
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(HALT);
}
PutModule("No such SCHAT to [" + sTarget + "]");
} else
p->Write(sMessage + "\n");
return(HALT);
}
return(CONTINUE);
}
virtual void RemoveMarker(const CString & sNick)
{
map< CString,pair< u_long,u_short > >::iterator it = m_siiWaitingChats.find(sNick);
if (it != m_siiWaitingChats.end())
m_siiWaitingChats.erase(it);
}
void SendToUser(const CString & sFrom, const CString & sText)
{
//:*schat!znc@znc.in PRIVMSG Jim :
CString sSend = ":" + sFrom + " PRIVMSG " + m_pUser->GetCurNick() + " :" + sText;
PutUser(sSend);
}
bool IsAttached()
{
return(m_pUser->IsUserAttached());
}
private:
map< CString,pair< u_long,u_short > > m_siiWaitingChats;
CString m_sPemFile;
};
//////////////////// methods ////////////////
void CSChatSock::ReadLine(const CS_STRING & sLine)
{
if (m_pModule) {
CString sText = sLine;
if (sText[sText.length()-1] == '\n')
sText.erase(sText.length()-1, 1);
if (sText[sText.length()-1] == '\r')
sText.erase(sText.length()-1, 1);
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, "Secure cross platform (:P) chat system")