openclonk/src/network/C4Network2IRC.cpp

824 lines
23 KiB
C++
Raw Permalink Normal View History

2009-05-08 13:28:41 +00:00
/*
* OpenClonk, http://www.openclonk.org
*
* Copyright (c) 2001-2009, RedWolf Design GmbH, http://www.clonk.de/
2016-04-03 18:18:29 +00:00
* Copyright (c) 2009-2016, The OpenClonk Team and contributors
2009-05-08 13:28:41 +00:00
*
* Distributed under the terms of the ISC license; see accompanying file
* "COPYING" for details.
2009-05-08 13:28:41 +00:00
*
* "Clonk" is a registered trademark of Matthes Bender, used with permission.
* See accompanying file "TRADEMARK" for details.
2009-05-08 13:28:41 +00:00
*
* To redistribute this file separately, substitute the full license texts
* for the above references.
2009-05-08 13:28:41 +00:00
*/
2009-05-08 13:28:41 +00:00
#include "C4Include.h"
#include "network/C4Network2IRC.h"
2009-05-08 13:28:41 +00:00
#include "C4Version.h"
#include "game/C4Application.h"
#include "gui/C4Gui.h" // for clearly visi
#include "network/C4InteractiveThread.h"
2009-05-08 13:28:41 +00:00
// Helper for IRC command parameter parsing
StdStrBuf ircExtractPar(const char **ppPar)
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// No parameter left?
2010-03-28 18:58:01 +00:00
if (!ppPar || !*ppPar || !**ppPar)
2009-05-08 13:28:41 +00:00
return StdStrBuf("");
// Last parameter?
StdStrBuf Result;
2010-03-28 18:58:01 +00:00
if (**ppPar == ':')
{
2009-05-08 13:28:41 +00:00
// Reference everything after the double-colon
Result.Ref(*ppPar + 1);
*ppPar = nullptr;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
else
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// Copy until next space (or end of string)
Result.CopyUntil(*ppPar, ' ');
// Go over parameters
*ppPar += Result.getLength();
2010-03-28 18:58:01 +00:00
if (**ppPar == ' ')
2009-05-08 13:28:41 +00:00
(*ppPar)++;
else
*ppPar = nullptr;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
// Done
return Result;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
// *** C4Network2IRCUser
C4Network2IRCUser::C4Network2IRCUser(const char *szName)
2010-03-28 18:58:01 +00:00
: Name(szName)
{
2009-05-08 13:28:41 +00:00
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
// *** C4Network2IRCChannel
C4Network2IRCChannel::C4Network2IRCChannel(const char *szName)
: Name(szName), pUsers(nullptr), fReceivingUsers(false)
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
C4Network2IRCChannel::~C4Network2IRCChannel()
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
ClearUsers();
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
C4Network2IRCUser *C4Network2IRCChannel::getUser(const char *szName) const
2010-03-28 18:58:01 +00:00
{
for (C4Network2IRCUser *pUser = pUsers; pUser; pUser = pUser->Next)
if (SEqual(pUser->getName(), szName))
2009-05-08 13:28:41 +00:00
return pUser;
return nullptr;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
void C4Network2IRCChannel::OnUsers(const char *szUsers, const char *szPrefixes)
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// Find actual prefixes
szPrefixes = SSearch(szPrefixes, ")");
// Reconstructs the list
2010-03-28 18:58:01 +00:00
if (!fReceivingUsers)
2009-05-08 13:28:41 +00:00
ClearUsers();
2010-03-28 18:58:01 +00:00
while (szUsers && *szUsers)
{
2009-05-08 13:28:41 +00:00
// Get user name
StdStrBuf PrefixedName = ircExtractPar(&szUsers);
// Remove prefix(es)
const char *szName = PrefixedName.getData();
2010-03-28 18:58:01 +00:00
if (szPrefixes)
while (strchr(szPrefixes, *szName))
2009-05-08 13:28:41 +00:00
szName++;
// Copy prefix
StdStrBuf Prefix;
Prefix.Copy(PrefixedName.getData(), szName - PrefixedName.getData());
// Add user
AddUser(szName)->SetPrefix(Prefix.getData());
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
// Set flag the user list won't get cleared again until OnUsersEnd is called
fReceivingUsers = true;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
void C4Network2IRCChannel::OnUsersEnd()
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// Reset flag
fReceivingUsers = false;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
void C4Network2IRCChannel::OnTopic(const char *szTopic)
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
Topic = szTopic;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
void C4Network2IRCChannel::OnKick(const char *szUser, const char *szComment)
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// Remove named user from channel list
C4Network2IRCUser *pUser = getUser(szUser);
2010-03-28 18:58:01 +00:00
if (pUser) DeleteUser(pUser);
}
2009-05-08 13:28:41 +00:00
void C4Network2IRCChannel::OnPart(const char *szUser, const char *szComment)
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// Remove named user from channel list
C4Network2IRCUser *pUser = getUser(szUser);
2010-03-28 18:58:01 +00:00
if (pUser) DeleteUser(pUser);
}
2009-05-08 13:28:41 +00:00
void C4Network2IRCChannel::OnJoin(const char *szUser)
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// Add user (do not set prefix)
2010-03-28 18:58:01 +00:00
if (!getUser(szUser))
2009-05-08 13:28:41 +00:00
AddUser(szUser);
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
C4Network2IRCUser *C4Network2IRCChannel::AddUser(const char *szName)
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// Check if the user already exists
C4Network2IRCUser *pUser = getUser(szName);
2010-03-28 18:58:01 +00:00
if (pUser) return pUser;
2009-05-08 13:28:41 +00:00
// Add to list
pUser = new C4Network2IRCUser(szName);
pUser->Next = pUsers;
pUsers = pUser;
return pUser;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
void C4Network2IRCChannel::DeleteUser(C4Network2IRCUser *pUser)
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// Unlink
2010-03-28 18:58:01 +00:00
if (pUser == pUsers)
2009-05-08 13:28:41 +00:00
pUsers = pUser->Next;
else
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
C4Network2IRCUser *pPrev = pUsers;
2010-03-28 18:58:01 +00:00
while (pPrev && pPrev->Next != pUser)
2009-05-08 13:28:41 +00:00
pPrev = pPrev->Next;
2010-03-28 18:58:01 +00:00
if (pPrev)
2009-05-08 13:28:41 +00:00
pPrev->Next = pUser->Next;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
// Delete
delete pUser;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
void C4Network2IRCChannel::ClearUsers()
2010-03-28 18:58:01 +00:00
{
while (pUsers)
2009-05-08 13:28:41 +00:00
DeleteUser(pUsers);
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
// *** C4Network2IRCClient
2011-03-21 21:39:36 +00:00
// Created statically in C4Application.cpp, refer by &Application.IRCClient
2009-05-08 13:28:41 +00:00
C4Network2IRCClient::C4Network2IRCClient() = default;
2009-05-08 13:28:41 +00:00
C4Network2IRCClient::~C4Network2IRCClient()
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
Close();
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
void C4Network2IRCClient::PackPacket(const C4NetIOPacket &rPacket, StdBuf &rOutBuf)
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// Enlarge buffer
int iSize = rPacket.getSize(),
2010-03-28 18:58:01 +00:00
iPos = rOutBuf.getSize();
2009-05-08 13:28:41 +00:00
rOutBuf.Grow(iSize + 2);
// Write packet
rOutBuf.Write(rPacket, iPos);
// Terminate
uint8_t *pPos = getMBufPtr<uint8_t>(rOutBuf, iPos + iSize);
*pPos = '\r'; *(pPos + 1) = '\n';
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
size_t C4Network2IRCClient::UnpackPacket(const StdBuf &rInBuf, const C4NetIO::addr_t &addr)
2010-03-28 18:58:01 +00:00
{
2010-04-01 21:08:06 +00:00
// Find line separation
2010-03-28 18:58:01 +00:00
const char *pSep = reinterpret_cast<const char *>(memchr(rInBuf.getData(), '\n', rInBuf.getSize()));
if (!pSep)
2009-05-08 13:28:41 +00:00
return 0;
2010-04-01 21:08:06 +00:00
// Check if it's actually correct separation (rarely the case)
2009-05-08 13:28:41 +00:00
int iSize = pSep - getBufPtr<char>(rInBuf) + 1,
2010-03-28 18:58:01 +00:00
iLength = iSize - 1;
if (iLength && *(pSep - 1) == '\r')
2009-05-08 13:28:41 +00:00
iLength--;
// Copy the line
StdStrBuf Buf; Buf.Copy(getBufPtr<char>(rInBuf), iLength);
// Ignore prefix
const char *pMsg = Buf.getData();
StdStrBuf Prefix;
2010-03-28 18:58:01 +00:00
if (*pMsg == ':')
{
2009-05-08 13:28:41 +00:00
Prefix.CopyUntil(pMsg + 1, ' ');
pMsg += Prefix.getLength() + 1;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
// Strip whitespace
2010-03-28 18:58:01 +00:00
while (*pMsg == ' ')
2009-05-08 13:28:41 +00:00
pMsg++;
// Ignore empty message
2010-03-28 18:58:01 +00:00
if (!*pMsg)
2009-05-08 13:28:41 +00:00
return iSize;
// Get command
StdStrBuf Cmd; Cmd.CopyUntil(pMsg, ' ');
// Precess command
const char *szParameters = SSearch(pMsg, " ");
OnCommand(Prefix.getData(), Cmd.getData(), szParameters ? szParameters : "");
// Consume the line
return iSize;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
bool C4Network2IRCClient::OnConn(const C4NetIO::addr_t &AddrPeer, const C4NetIO::addr_t &AddrConnect, const addr_t *pOwnAddr, C4NetIO *pNetIO)
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// Security checks
2013-02-18 14:30:00 +00:00
if (!fConnecting || fConnected || AddrConnect != ServerAddr) return false;
2009-05-08 13:28:41 +00:00
CStdLock Lock(&CSec);
// Save connection data
fConnected = true;
fConnecting = false;
C4Network2IRCClient::PeerAddr = AddrPeer;
// Send welcome message
2010-03-28 18:58:01 +00:00
if (!Password.isNull())
2009-05-08 13:28:41 +00:00
Send("PASS", Password.getData());
Send("NICK", Nick.getData());
Send("USER", FormatString("clonk x x :%s", RealName.getLength() ? RealName.getData() : " ").getData());
2009-05-08 13:28:41 +00:00
// Okay
return true;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
void C4Network2IRCClient::OnDisconn(const C4NetIO::addr_t &AddrPeer, C4NetIO *pNetIO, const char *szReason)
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
fConnected = false;
// Show a message with the reason
PushMessage(MSG_Status, "", Nick.getData(), FormatString(LoadResStr("IDS_MSG_DISCONNECTEDFROMSERVER"), szReason).getData());
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
void C4Network2IRCClient::OnPacket(const class C4NetIOPacket &rPacket, C4NetIO *pNetIO)
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// Won't get called
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
C4Network2IRCChannel *C4Network2IRCClient::getFirstChannel() const
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
return pChannels;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
C4Network2IRCChannel *C4Network2IRCClient::getNextChannel(C4Network2IRCChannel *pPrevChan) const
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
return pPrevChan ? pPrevChan->Next : pChannels;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
C4Network2IRCChannel *C4Network2IRCClient::getChannel(const char *szName) const
2010-03-28 18:58:01 +00:00
{
for (C4Network2IRCChannel *pChan = pChannels; pChan; pChan = pChan->Next)
if (SEqualNoCase(pChan->getName(), szName))
2009-05-08 13:28:41 +00:00
return pChan;
return nullptr;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
void C4Network2IRCClient::ClearMessageLog()
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// Clear log
2010-03-28 18:58:01 +00:00
while (iLogLength)
2009-05-08 13:28:41 +00:00
PopMessage();
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
void C4Network2IRCClient::MarkMessageLogRead()
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// set read marker to last message
pLogLastRead = pLogEnd;
iUnreadLogLength = 0;
// message buffer is smaller for messages already read: Remove old ones
2010-03-28 18:58:01 +00:00
while (iLogLength > C4NetIRCMaxReadLogLength)
2009-05-08 13:28:41 +00:00
PopMessage();
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
bool C4Network2IRCClient::Connect(const char *szServer, const char *szNick, const char *szRealName, const char *szPassword, const char *szAutoJoin)
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// Already connected? Close connection
2010-03-28 18:58:01 +00:00
if (fConnecting || fConnected)
2009-05-08 13:28:41 +00:00
Close();
// Initialize
C4NetIOTCP::SetCallback(this);
2010-03-28 18:58:01 +00:00
if (!Init())
2009-05-08 13:28:41 +00:00
return false;
// Resolve address
ServerAddr.SetAddress(StdStrBuf(szServer));
if (ServerAddr.IsNull())
2009-05-08 13:28:41 +00:00
{ SetError("Could no resolve server address!"); return false; }
ServerAddr.SetDefaultPort(6666);
2009-05-08 13:28:41 +00:00
// Set connection data
Nick = szNick; RealName = szRealName;
Password = szPassword; AutoJoin = szAutoJoin;
// Truncate password
2010-03-28 18:58:01 +00:00
if (Password.getLength() > 31)
2009-05-08 13:28:41 +00:00
Password.SetLength(31);
// Start connecting
2010-03-28 18:58:01 +00:00
if (!C4NetIOTCP::Connect(ServerAddr))
2009-05-08 13:28:41 +00:00
return false;
// Reset status data
Prefixes = "(ov)@+";
// Okay, let's wait for the connection.
fConnecting = true;
return true;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
bool C4Network2IRCClient::Close()
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// Close network
C4NetIOTCP::Close();
2011-03-21 21:39:36 +00:00
// Save & Clear channels
if(pChannels) // Don't override empty
{
// It's somewhat weird to loop backward through a singly linked list, but it's necessary to keep the order
StdStrBuf chanstr;
C4Network2IRCChannel * pChan = pChannels;
while(pChan->Next)
pChan = pChan->Next;
chanstr.Append(pChan->getName());
while (pChan != pChannels)
{
C4Network2IRCChannel * pChanPrev = pChannels;
while(pChanPrev->Next != pChan)
pChanPrev = pChanPrev->Next;
pChan = pChanPrev;
chanstr.Append(",");
chanstr.Append(pChan->getName());
}
strncpy(Config.IRC.Channel, chanstr.getData(), sizeof(Config.IRC.Channel)-1);
}
2010-03-28 18:58:01 +00:00
while (pChannels)
2009-05-08 13:28:41 +00:00
DeleteChannel(pChannels);
// Clear log
ClearMessageLog();
// Reset flags
fConnected = fConnecting = false;
return true;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
bool C4Network2IRCClient::Send(const char *szCommand, const char *szParameters)
2010-03-28 18:58:01 +00:00
{
if (!fConnected)
2009-05-08 13:28:41 +00:00
{ SetError("not connected"); return false; }
// Create message
StdStrBuf Msg;
2010-03-28 18:58:01 +00:00
if (szParameters)
2009-05-08 13:28:41 +00:00
Msg.Format("%s %s", szCommand, szParameters);
else
Msg.Ref(szCommand);
// Send
return C4NetIOTCP::Send(C4NetIOPacket(Msg.getData(), Msg.getLength(), false, PeerAddr));
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
bool C4Network2IRCClient::Quit(const char *szReason)
2010-03-28 18:58:01 +00:00
{
if (!Send("QUIT", FormatString(":%s", szReason).getData()))
2009-05-08 13:28:41 +00:00
return false;
// Must be last message
return Close();
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
bool C4Network2IRCClient::Join(const char *szChannel)
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// Newbie limitation: can only join channels beginning with #clonk
if (!Config.IRC.AllowAllChannels)
if (!SWildcardMatchEx(szChannel, "#*clonk*"))
2009-05-08 13:28:41 +00:00
{
const char* message = LoadResStr("IDS_ERR_CHANNELNOTALLOWED");
PushMessage(MSG_Status, "", "", message);
2009-05-08 13:28:41 +00:00
SetError("Joining this channel not allowed");
Application.InteractiveThread.ThreadPostAsync<bool>(std::bind(&C4GUI::Screen::ShowMessage, ::pGUI, message, LoadResStr("IDS_DLG_CHAT"), C4GUI::Ico_Error, static_cast<int32_t* const &>(nullptr)));
2009-05-08 13:28:41 +00:00
return false;
}
return Send("JOIN", szChannel);
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
bool C4Network2IRCClient::Part(const char *szChannel)
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
return Send("PART", szChannel);
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
bool C4Network2IRCClient::Message(const char *szTarget, const char *szText)
2010-03-28 18:58:01 +00:00
{
if (!Send("PRIVMSG", FormatString("%s :%s", szTarget, szText).getData()))
return false;
PushMessage(MSG_Message, Nick.getData(), szTarget, szText);
return true;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
bool C4Network2IRCClient::Notice(const char *szTarget, const char *szText)
2010-03-28 18:58:01 +00:00
{
if (!Send("NOTICE", FormatString("%s :%s", szTarget, szText).getData()))
return false;
PushMessage(MSG_Notice, Nick.getData(), szTarget, szText);
return true;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
bool C4Network2IRCClient::Action(const char *szTarget, const char *szText)
2010-03-28 18:58:01 +00:00
{
if (!Send("PRIVMSG", FormatString("%s :\1ACTION %s\1", szTarget, szText).getData()))
return false;
PushMessage(MSG_Action, Nick.getData(), szTarget, szText);
return true;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
bool C4Network2IRCClient::ChangeNick(const char *szNewNick)
2010-03-28 18:58:01 +00:00
{
return Send("NICK", szNewNick);
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
bool C4Network2IRCClient::RegisterNick(const char *szPassword, const char *szMail)
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
return Send("PRIVMSG", FormatString("NickServ :REGISTER %s %s", szPassword, szMail).getData());
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
void C4Network2IRCClient::OnCommand(const char *szSender, const char *szCommand, const char *szParameters)
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
CStdLock Lock(&CSec);
// Numeric command?
2010-03-28 18:58:01 +00:00
if (isdigit((unsigned char)*szCommand) && SLen(szCommand) == 3)
{
2009-05-08 13:28:41 +00:00
OnNumericCommand(szSender, atoi(szCommand), szParameters);
return;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
// Sender's nick
StdStrBuf SenderNick;
2010-03-28 18:58:01 +00:00
if (szSender) SenderNick.CopyUntil(szSender, '!');
2009-05-08 13:28:41 +00:00
// Ping?
2010-03-28 18:58:01 +00:00
if (SEqualNoCase(szCommand, "PING"))
2009-05-08 13:28:41 +00:00
Send("PONG", szParameters);
// Message?
2010-03-28 18:58:01 +00:00
if (SEqualNoCase(szCommand, "NOTICE") || SEqualNoCase(szCommand, "PRIVMSG"))
{
2009-05-08 13:28:41 +00:00
// Get target
StdStrBuf Target = ircExtractPar(&szParameters);
// Get text
StdStrBuf Text = ircExtractPar(&szParameters);
// Process message
OnMessage(SEqualNoCase(szCommand, "NOTICE"), szSender, Target.getData(), Text.getData());
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
// Channel join?
2010-03-28 18:58:01 +00:00
if (SEqualNoCase(szCommand, "JOIN"))
{
2009-05-08 13:28:41 +00:00
// Get channel
StdStrBuf Channel = ircExtractPar(&szParameters);
C4Network2IRCChannel *pChan = AddChannel(Channel.getData());
// Add user
pChan->OnJoin(SenderNick.getData());
// Myself?
2010-03-28 18:58:01 +00:00
if (SenderNick == Nick)
2009-05-08 13:28:41 +00:00
PushMessage(MSG_Status, szSender, Channel.getData(), FormatString(LoadResStr("IDS_MSG_YOUHAVEJOINEDCHANNEL"), Channel.getData()).getData());
else
PushMessage(MSG_Status, szSender, Channel.getData(), FormatString(LoadResStr("IDS_MSG_HASJOINEDTHECHANNEL"), SenderNick.getData()).getData());
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
// Channel part?
2010-03-28 18:58:01 +00:00
if (SEqualNoCase(szCommand, "PART"))
{
2009-05-08 13:28:41 +00:00
// Get channel
StdStrBuf Channel = ircExtractPar(&szParameters);
C4Network2IRCChannel *pChan = AddChannel(Channel.getData());
// Get message
StdStrBuf Comment = ircExtractPar(&szParameters);
// Remove user
pChan->OnPart(SenderNick.getData(), Comment.getData());
// Myself?
2010-03-28 18:58:01 +00:00
if (SenderNick == Nick)
{
2009-05-08 13:28:41 +00:00
DeleteChannel(pChan);
PushMessage(MSG_Status, szSender, Nick.getData(), FormatString(LoadResStr("IDS_MSG_YOUHAVELEFTCHANNEL"), Channel.getData(), Comment.getData()).getData());
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
else
PushMessage(MSG_Status, szSender, Channel.getData(), FormatString(LoadResStr("IDS_MSG_HASLEFTTHECHANNEL"), SenderNick.getData(), Comment.getData()).getData());
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
// Kick?
2010-03-28 18:58:01 +00:00
if (SEqualNoCase(szCommand, "KICK"))
{
2009-05-08 13:28:41 +00:00
// Get channel
StdStrBuf Channel = ircExtractPar(&szParameters);
C4Network2IRCChannel *pChan = AddChannel(Channel.getData());
// Get kicked user
StdStrBuf Kicked = ircExtractPar(&szParameters);
// Get message
StdStrBuf Comment = ircExtractPar(&szParameters);
// Remove user
pChan->OnKick(Kicked.getData(), Comment.getData());
// Myself?
2010-03-28 18:58:01 +00:00
if (Kicked == Nick)
{
2009-05-08 13:28:41 +00:00
DeleteChannel(pChan);
PushMessage(MSG_Status, szSender, Nick.getData(), FormatString(LoadResStr("IDS_MSG_YOUWEREKICKEDFROMCHANNEL"), Channel.getData(), Comment.getData()).getData());
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
else
PushMessage(MSG_Status, szSender, Channel.getData(), FormatString(LoadResStr("IDS_MSG_WASKICKEDFROMTHECHANNEL"), Kicked.getData(), Comment.getData()).getData());
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
// Quit?
2010-03-28 18:58:01 +00:00
if (SEqualNoCase(szCommand, "QUIT"))
{
2009-05-08 13:28:41 +00:00
// Get comment
StdStrBuf Comment = ircExtractPar(&szParameters);
// Format status message
StdStrBuf Message = FormatString(LoadResStr("IDS_MSG_HASDISCONNECTED"), SenderNick.getData(), Comment.getData());
// Remove him from all channels
2010-03-28 18:58:01 +00:00
for (C4Network2IRCChannel *pChan = pChannels; pChan; pChan = pChan->Next)
if (pChan->getUser(SenderNick.getData()))
{
pChan->OnPart(SenderNick.getData(), "Quit");
PushMessage(MSG_Status, szSender, pChan->getName(), Message.getData());
2010-03-28 18:58:01 +00:00
}
}
2009-05-08 13:28:41 +00:00
// Topic change?
2010-03-28 18:58:01 +00:00
if (SEqualNoCase(szCommand, "TOPIC"))
{
2009-05-08 13:28:41 +00:00
// Get channel and topic
StdStrBuf Channel = ircExtractPar(&szParameters);
StdStrBuf Topic = ircExtractPar(&szParameters);
// Set topic
AddChannel(Channel.getData())->OnTopic(Topic.getData());
// Message
PushMessage(MSG_Status, szSender, Channel.getData(), FormatString(LoadResStr("IDS_MSG_CHANGESTHETOPICTO"), SenderNick.getData(), Topic.getData()).getData());
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
// Mode?
2010-03-28 18:58:01 +00:00
if (SEqualNoCase(szCommand, "MODE"))
{
2009-05-08 13:28:41 +00:00
// Get all data
StdStrBuf Channel = ircExtractPar(&szParameters);
StdStrBuf Flags = ircExtractPar(&szParameters);
StdStrBuf What = ircExtractPar(&szParameters);
// Make sure it's a channel
C4Network2IRCChannel *pChan = getChannel(Channel.getData());
2010-03-28 18:58:01 +00:00
if (pChan)
2009-05-08 13:28:41 +00:00
// Ask for names, because user prefixes might be out of sync
Send("NAMES", Channel.getData());
// Show Message
PushMessage(MSG_Status, szSender, Channel.getData(), FormatString(LoadResStr("IDS_MSG_SETSMODE"), SenderNick.getData(), Flags.getData(), What.getData()).getData());
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
// Error?
2010-03-28 18:58:01 +00:00
if (SEqualNoCase(szCommand, "ERROR"))
{
2009-05-08 13:28:41 +00:00
// Get message
StdStrBuf Message = ircExtractPar(&szParameters);
// Push it
PushMessage(MSG_Server, szSender, Nick.getData(), Message.getData());
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
// Nickchange?
2010-03-28 18:58:01 +00:00
if (SEqualNoCase(szCommand, "NICK"))
{
2009-05-08 13:28:41 +00:00
// Get new nick
StdStrBuf NewNick = ircExtractPar(&szParameters);
// Format status message
StdStrBuf Message = FormatString(LoadResStr("IDS_MSG_ISNOWKNOWNAS"), SenderNick.getData(), NewNick.getData());
// Rename on all channels
2010-03-28 18:58:01 +00:00
for (C4Network2IRCChannel *pChan = pChannels; pChan; pChan = pChan->Next)
if (pChan->getUser(SenderNick.getData()))
{
pChan->OnPart(SenderNick.getData(), "Nickchange");
pChan->OnJoin(NewNick.getData());
PushMessage(MSG_Status, szSender, pChan->getName(), Message.getData());
2010-03-28 18:58:01 +00:00
}
// Self?
2010-03-28 18:58:01 +00:00
if (SenderNick == Nick)
Nick = NewNick;
2009-05-08 13:28:41 +00:00
}
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
void C4Network2IRCClient::OnNumericCommand(const char *szSender, int iCommand, const char *szParameters)
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
bool fShowMessage = true;
// Get target
StdStrBuf Target = ircExtractPar(&szParameters);
// Handle command
2010-03-28 18:58:01 +00:00
switch (iCommand)
{
2009-05-08 13:28:41 +00:00
2010-03-28 18:58:01 +00:00
case 433: // Nickname already in use
{
StdStrBuf DesiredNick = ircExtractPar(&szParameters);
// Automatically try to choose a new one
DesiredNick.AppendChar('_');
Send("NICK", DesiredNick.getData());
break;
}
2009-05-08 13:28:41 +00:00
2010-03-28 18:58:01 +00:00
case 376: // End of MOTD
case 422: // MOTD missing
// Let's take this a sign that the connection is established.
OnConnected();
break;
2009-05-08 13:28:41 +00:00
2010-03-28 18:58:01 +00:00
case 331: // No topic set
case 332: // Topic notify / change
{
// Get Channel name and topic
StdStrBuf Channel = ircExtractPar(&szParameters);
StdStrBuf Topic = (iCommand == 332 ? ircExtractPar(&szParameters) : StdStrBuf(""));
// Set it
AddChannel(Channel.getData())->OnTopic(Topic.getData());
// Log
if (Topic.getLength())
PushMessage(MSG_Status, szSender, Channel.getData(), FormatString(LoadResStr("IDS_MSG_TOPICIN"), Channel.getData(), Topic.getData()).getData());
}
break;
2009-05-08 13:28:41 +00:00
2010-03-28 18:58:01 +00:00
case 333: // Last topic change
fShowMessage = false; // ignore
break;
2009-05-08 13:28:41 +00:00
2010-03-28 18:58:01 +00:00
case 353: // Names in channel
{
// Get Channel name and name list
StdStrBuf Junk = ircExtractPar(&szParameters); // ??!
StdStrBuf Channel = ircExtractPar(&szParameters);
StdStrBuf Names = ircExtractPar(&szParameters);
// Set it
AddChannel(Channel.getData())->OnUsers(Names.getData(), Prefixes.getData());
fShowMessage = false;
}
break;
2009-05-08 13:28:41 +00:00
2010-03-28 18:58:01 +00:00
case 366: // End of names list
{
// Get Channel name
StdStrBuf Channel = ircExtractPar(&szParameters);
// Finish
AddChannel(Channel.getData())->OnUsersEnd();
fShowMessage = false;
// Notify
if (pNotify) pNotify->PushEvent(Ev_IRC_Message, this);
}
break;
2009-05-08 13:28:41 +00:00
2010-03-28 18:58:01 +00:00
case 4: // Server version
fShowMessage = false; // ignore
break;
2009-05-08 13:28:41 +00:00
2010-03-28 18:58:01 +00:00
case 5: // Server support string
{
while (szParameters && *szParameters)
{
// Get support-token.
StdStrBuf Token = ircExtractPar(&szParameters);
StdStrBuf Parameter; Parameter.CopyUntil(Token.getData(), '=');
// Check if it's interesting and safe data if so.
if (SEqualNoCase(Parameter.getData(), "PREFIX"))
Prefixes.Copy(SSearch(Token.getData(), "="));
2009-05-08 13:28:41 +00:00
}
2010-03-28 18:58:01 +00:00
fShowMessage = false;
}
break;
}
2009-05-08 13:28:41 +00:00
// Show embedded message, if any?
2010-03-28 18:58:01 +00:00
if (fShowMessage)
{
2009-05-08 13:28:41 +00:00
// Check if first parameter is some sort of channel name
C4Network2IRCChannel *pChannel = nullptr;
2010-03-28 18:58:01 +00:00
if (szParameters && *szParameters && *szParameters != ':')
2009-05-08 13:28:41 +00:00
pChannel = getChannel(ircExtractPar(&szParameters).getData());
// Go over other parameters
const char *pMsg = szParameters;
2010-03-28 18:58:01 +00:00
while (pMsg && *pMsg && *pMsg != ':')
2009-05-08 13:28:41 +00:00
pMsg = SSearch(pMsg, " ");
// Show it
2010-03-28 18:58:01 +00:00
if (pMsg && *pMsg)
{
if (!pChannel)
2009-05-08 13:28:41 +00:00
PushMessage(MSG_Server, szSender, Nick.getData(), pMsg + 1);
else
PushMessage(MSG_Status, szSender, pChannel->getName(), pMsg + 1);
}
}
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
void C4Network2IRCClient::OnConnected()
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// Set flag
fConnected = true;
// Try to join channel(s)
2010-03-28 18:58:01 +00:00
if (AutoJoin.getLength())
2009-05-08 13:28:41 +00:00
Join(AutoJoin.getData());
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
void C4Network2IRCClient::OnMessage(bool fNotice, const char *szSender, const char *szTarget, const char *szText)
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// CTCP tagged data?
const char X_DELIM = '\001';
2010-03-28 18:58:01 +00:00
if (szText[0] == X_DELIM)
{
2009-05-08 13:28:41 +00:00
// Process messages (it's very rarely more than one, but the spec allows it)
const char *pMsg = szText + 1;
2010-03-28 18:58:01 +00:00
while (*pMsg)
{
2009-05-08 13:28:41 +00:00
// Find end
const char *pEnd = strchr(pMsg, X_DELIM);
2010-03-28 18:58:01 +00:00
if (!pEnd) pEnd = pMsg + SLen(pMsg);
2009-05-08 13:28:41 +00:00
// Copy CTCP query/reply, get tag
StdStrBuf CTCP; CTCP.Copy(pMsg, pEnd - pMsg);
StdStrBuf Tag; Tag.CopyUntil(CTCP.getData(), ' ');
const char *szData = SSearch(CTCP.getData(), " ");
StdStrBuf Sender; Sender.CopyUntil(szSender, '!');
// Process
2010-03-28 18:58:01 +00:00
if (SEqualNoCase(Tag.getData(), "ACTION"))
2009-05-08 13:28:41 +00:00
PushMessage(MSG_Action, szSender, szTarget, szData ? szData : "");
2010-03-28 18:58:01 +00:00
if (SEqualNoCase(Tag.getData(), "FINGER") && !fNotice)
{
2009-05-08 13:28:41 +00:00
StdStrBuf Answer;
2010-03-28 18:58:01 +00:00
Answer = LoadResStr("IDS_PRC_UNREGUSER"); //ToDo: Anser sth. else
2009-05-08 13:28:41 +00:00
Send("NOTICE", FormatString("%s :%cFINGER %s%c",
2010-03-28 18:58:01 +00:00
Sender.getData(), X_DELIM,
Answer.getData(),
X_DELIM).getData());
}
if (SEqualNoCase(Tag.getData(), "VERSION") && !fNotice)
2009-05-08 13:28:41 +00:00
Send("NOTICE", FormatString("%s :%cVERSION " C4ENGINECAPTION ":" C4VERSION ":" C4_OS "%c",
2010-03-28 18:58:01 +00:00
Sender.getData(), X_DELIM, X_DELIM).getData());
if (SEqualNoCase(Tag.getData(), "PING") && !fNotice)
2009-05-08 13:28:41 +00:00
Send("NOTICE", FormatString("%s :%cPING %s%c",
2010-03-28 18:58:01 +00:00
Sender.getData(), X_DELIM, szData, X_DELIM).getData());
2009-05-08 13:28:41 +00:00
// Get next message
pMsg = pEnd;
2010-03-28 18:58:01 +00:00
if (*pMsg == X_DELIM) pMsg++;
2009-05-08 13:28:41 +00:00
}
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
// Standard message (not CTCP tagged): Push
else
PushMessage(fNotice ? MSG_Notice : MSG_Message, szSender, szTarget, szText);
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
void C4Network2IRCClient::PopMessage()
2010-03-28 18:58:01 +00:00
{
if (!iLogLength) return;
2009-05-08 13:28:41 +00:00
// Unlink message
C4Network2IRCMessage *pMsg = pLog;
pLog = pMsg->Next;
if (!pLog) pLogEnd = nullptr;
if (pLogLastRead == pMsg) pLogLastRead = nullptr;
2009-05-08 13:28:41 +00:00
// Delete it
delete pMsg;
iLogLength--;
if (iUnreadLogLength > iLogLength) iUnreadLogLength = iLogLength;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
void C4Network2IRCClient::PushMessage(C4Network2IRCMessageType eType, const char *szSource, const char *szTarget, const char *szText)
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// Create message
C4Network2IRCMessage *pMsg = new C4Network2IRCMessage(eType, szSource, szTarget, szText);
// Add to list
if (pLogEnd)
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
pLogEnd->Next = pMsg;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
else
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
pLog = pMsg;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
pLogEnd = pMsg;
// Count
iLogLength++;
iUnreadLogLength++;
2010-03-28 18:58:01 +00:00
while (iLogLength > C4NetIRCMaxLogLength)
2009-05-08 13:28:41 +00:00
PopMessage();
// Notify
2010-03-28 18:58:01 +00:00
if (pNotify)
pNotify->PushEvent(Ev_IRC_Message, this);
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
C4Network2IRCChannel *C4Network2IRCClient::AddChannel(const char *szName)
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// Already exists?
C4Network2IRCChannel *pChan = getChannel(szName);
2010-03-28 18:58:01 +00:00
if (pChan) return pChan;
2009-05-08 13:28:41 +00:00
// Create new, insert
pChan = new C4Network2IRCChannel(szName);
pChan->Next = pChannels;
pChannels = pChan;
return pChan;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
void C4Network2IRCClient::DeleteChannel(C4Network2IRCChannel *pChannel)
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// Unlink
2010-03-28 18:58:01 +00:00
if (pChannel == pChannels)
2009-05-08 13:28:41 +00:00
pChannels = pChannel->Next;
else
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
C4Network2IRCChannel *pPrev = pChannels;
2010-03-28 18:58:01 +00:00
while (pPrev && pPrev->Next != pChannel)
2009-05-08 13:28:41 +00:00
pPrev = pPrev->Next;
2010-03-28 18:58:01 +00:00
if (pPrev)
2009-05-08 13:28:41 +00:00
pPrev->Next = pChannel->Next;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
// Delete
delete pChannel;
2010-03-28 18:58:01 +00:00
}