You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
604 lines
12 KiB
604 lines
12 KiB
8 years ago
|
// irc.cpp
|
||
|
// Made by Adi Degani - http://www.codeguru.com/network/irc.html
|
||
|
#include <stdio.h>
|
||
|
#include <stdlib.h>
|
||
|
#include <Tchar.h>
|
||
|
#include "agglobal.h"
|
||
|
|
||
|
#include "irc.h"
|
||
|
|
||
|
using namespace irc;
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////
|
||
|
|
||
|
CIrcMessage::CIrcMessage(const char* lpszCmdLine, bool bIncoming)
|
||
|
: m_bIncoming(bIncoming)
|
||
|
{
|
||
|
ParseIrcCommand(lpszCmdLine);
|
||
|
}
|
||
|
|
||
|
CIrcMessage::CIrcMessage(const CIrcMessage& m)
|
||
|
: sCommand(m.sCommand),
|
||
|
parameters(m.parameters),
|
||
|
m_bIncoming(m.m_bIncoming)
|
||
|
{
|
||
|
prefix.sNick = m.prefix.sNick;
|
||
|
prefix.sUser = m.prefix.sUser;
|
||
|
prefix.sHost = m.prefix.sHost;
|
||
|
}
|
||
|
|
||
|
void CIrcMessage::Reset()
|
||
|
{
|
||
|
prefix.sNick = prefix.sUser = prefix.sHost = sCommand = "";
|
||
|
m_bIncoming = false;
|
||
|
parameters.clear();
|
||
|
}
|
||
|
|
||
|
CIrcMessage& CIrcMessage::operator = (const CIrcMessage& m)
|
||
|
{
|
||
|
if( &m != this )
|
||
|
{
|
||
|
sCommand = m.sCommand;
|
||
|
parameters = m.parameters;
|
||
|
prefix.sNick = m.prefix.sNick;
|
||
|
prefix.sUser = m.prefix.sUser;
|
||
|
prefix.sHost = m.prefix.sHost;
|
||
|
m_bIncoming = m.m_bIncoming;
|
||
|
}
|
||
|
return *this;
|
||
|
}
|
||
|
|
||
|
CIrcMessage& CIrcMessage::operator = (const char* lpszCmdLine)
|
||
|
{
|
||
|
Reset();
|
||
|
ParseIrcCommand(lpszCmdLine);
|
||
|
return *this;
|
||
|
}
|
||
|
|
||
|
void CIrcMessage::ParseIrcCommand(const char* lpszCmdLine)
|
||
|
{
|
||
|
const char* p1 = lpszCmdLine;
|
||
|
const char* p2 = lpszCmdLine;
|
||
|
|
||
|
assert(lpszCmdLine != NULL);
|
||
|
assert(*lpszCmdLine);
|
||
|
|
||
|
// prefix exists ?
|
||
|
if( *p1 == ':' )
|
||
|
{ // break prefix into its components (nick!user@host)
|
||
|
p2 = ++p1;
|
||
|
while( *p2 && !strchr(" !", *p2) )
|
||
|
++p2;
|
||
|
prefix.sNick.assign(p1, p2 - p1);
|
||
|
if( *p2 != '!' )
|
||
|
goto end_of_prefix;
|
||
|
p1 = ++p2;
|
||
|
while( *p2 && !strchr(" @", *p2) )
|
||
|
++p2;
|
||
|
prefix.sUser.assign(p1, p2 - p1);
|
||
|
if( *p2 != '@' )
|
||
|
goto end_of_prefix;
|
||
|
p1 = ++p2;
|
||
|
while( *p2 && !isspace(*p2) )
|
||
|
++p2;
|
||
|
prefix.sHost.assign(p1, p2 - p1);
|
||
|
end_of_prefix :
|
||
|
while( *p2 && isspace(*p2) )
|
||
|
++p2;
|
||
|
p1 = p2;
|
||
|
}
|
||
|
|
||
|
// get command
|
||
|
assert(*p1 != '\0');
|
||
|
p2 = p1;
|
||
|
while( *p2 && !isspace(*p2) )
|
||
|
++p2;
|
||
|
sCommand.assign(p1, p2 - p1);
|
||
|
_strupr((char*)sCommand.c_str());
|
||
|
while( *p2 && isspace(*p2) )
|
||
|
++p2;
|
||
|
p1 = p2;
|
||
|
|
||
|
// get parameters
|
||
|
while( *p1 )
|
||
|
{
|
||
|
if( *p1 == ':' )
|
||
|
{
|
||
|
++p1;
|
||
|
// seek end-of-message
|
||
|
while( *p2 )
|
||
|
++p2;
|
||
|
parameters.push_back(AgString(p1, p2 - p1));
|
||
|
break;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// seek end of parameter
|
||
|
while( *p2 && !isspace(*p2) )
|
||
|
++p2;
|
||
|
parameters.push_back(AgString(p1, p2 - p1));
|
||
|
// see next parameter
|
||
|
while( *p2 && isspace(*p2) )
|
||
|
++p2;
|
||
|
p1 = p2;
|
||
|
}
|
||
|
} // end parameters loop
|
||
|
}
|
||
|
|
||
|
AgString CIrcMessage::AsString() const
|
||
|
{
|
||
|
AgString s;
|
||
|
|
||
|
if( prefix.sNick.length() )
|
||
|
{
|
||
|
s += ":" + prefix.sNick;
|
||
|
if( prefix.sUser.length() && prefix.sHost.length() )
|
||
|
s += "!" + prefix.sUser + "@" + prefix.sHost;
|
||
|
s += " ";
|
||
|
}
|
||
|
|
||
|
s += sCommand;
|
||
|
|
||
|
for(unsigned int i=0; i < parameters.size(); i++)
|
||
|
{
|
||
|
s += " ";
|
||
|
if( i == parameters.size() - 1 ) // is last parameter ?
|
||
|
s += ":";
|
||
|
s += parameters[i];
|
||
|
}
|
||
|
|
||
|
s += endl;
|
||
|
|
||
|
return s;
|
||
|
}
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////
|
||
|
|
||
|
CIrcSession::CIrcSession(IIrcSessionMonitor* pMonitor)
|
||
|
: m_hThread(NULL)
|
||
|
{
|
||
|
InitializeCriticalSection(&m_cs);
|
||
|
}
|
||
|
|
||
|
CIrcSession::~CIrcSession()
|
||
|
{
|
||
|
Disconnect();
|
||
|
DeleteCriticalSection(&m_cs);
|
||
|
}
|
||
|
|
||
|
bool CIrcSession::Connect(const CIrcSessionInfo& info)
|
||
|
{
|
||
|
assert(m_hThread==NULL && !(bool)m_socket);
|
||
|
|
||
|
try
|
||
|
{
|
||
|
m_identServer.Start(info.sUserID.c_str());
|
||
|
|
||
|
if( !m_socket.Create() )
|
||
|
throw "Failed to create socket!";
|
||
|
|
||
|
InetAddr addr(info.sServer.c_str(), (WORD)info.iPort);
|
||
|
if( !m_socket.Connect(addr) )
|
||
|
{
|
||
|
m_socket.Close();
|
||
|
throw "Failed to connect to host!";
|
||
|
}
|
||
|
|
||
|
m_info = info;
|
||
|
|
||
|
// start receiving messages from host
|
||
|
DWORD dwThreadId = 0;
|
||
|
m_hThread = CreateThread(NULL, 0, ThreadProc, this, 0, &dwThreadId);
|
||
|
Sleep(100);
|
||
|
|
||
|
if( info.sPassword.length() )
|
||
|
m_socket.Send("PASS %s\r\n", info.sPassword.c_str());
|
||
|
|
||
|
m_socket.Send("NICK %s\r\n", info.sNick.c_str());
|
||
|
|
||
|
TCHAR szHostName[MAX_PATH];
|
||
|
DWORD cbHostName = sizeof(szHostName);
|
||
|
GetComputerName(szHostName, &cbHostName);
|
||
|
|
||
|
m_socket.Send("USER %s %s %s :%s\r\n",
|
||
|
info.sUserID.c_str(), szHostName, "server", info.sFullName.c_str());
|
||
|
}
|
||
|
catch( const char* )
|
||
|
{
|
||
|
Disconnect();
|
||
|
}
|
||
|
catch( ... )
|
||
|
{
|
||
|
Disconnect();
|
||
|
}
|
||
|
|
||
|
return (bool)m_socket;
|
||
|
}
|
||
|
|
||
|
void CIrcSession::Disconnect(const char* lpszMessage)
|
||
|
{
|
||
|
static const DWORD dwServerTimeout = 3 * 1000;
|
||
|
|
||
|
if( !m_hThread )
|
||
|
return;
|
||
|
|
||
|
m_socket.Send("QUIT :%s\r\n", lpszMessage ? lpszMessage : "bye!");
|
||
|
|
||
|
if( m_hThread && WaitForSingleObject(m_hThread, dwServerTimeout) != WAIT_OBJECT_0 )
|
||
|
{
|
||
|
m_socket.Close();
|
||
|
Sleep(100);
|
||
|
if( m_hThread && WaitForSingleObject(m_hThread, dwServerTimeout) != WAIT_OBJECT_0 )
|
||
|
{
|
||
|
TerminateThread(m_hThread, 1);
|
||
|
CloseHandle(m_hThread);
|
||
|
m_hThread = NULL;
|
||
|
m_info.Reset();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void CIrcSession::Notify(const CIrcMessage* pmsg)
|
||
|
{
|
||
|
// forward message to monitor objects
|
||
|
EnterCriticalSection(&m_cs);
|
||
|
for(CIrcSessionMonitors::iterator it = m_monitors.begin();
|
||
|
it != m_monitors.end();
|
||
|
it++
|
||
|
)
|
||
|
{
|
||
|
(*it)->OnIrcMessage(pmsg);
|
||
|
}
|
||
|
LeaveCriticalSection(&m_cs);
|
||
|
}
|
||
|
|
||
|
void CIrcSession::DoReceive()
|
||
|
{
|
||
|
// CIrcIdentServer m_identServer;
|
||
|
char chBuf[1024*4+1];
|
||
|
int cbInBuf = 0;
|
||
|
|
||
|
// if( m_info.bIdentServer )
|
||
|
//m_identServer.Start(m_info.sUserID.c_str());
|
||
|
//Sleep(100);
|
||
|
|
||
|
while( m_socket )
|
||
|
{
|
||
|
int cbRead;
|
||
|
int nLinesProcessed = 0;
|
||
|
|
||
|
cbRead = m_socket.Receive((unsigned char*)chBuf+cbInBuf, sizeof(chBuf)-cbInBuf-1);
|
||
|
if( cbRead <= 0 )
|
||
|
break;
|
||
|
cbInBuf += cbRead;
|
||
|
chBuf[cbInBuf] = '\0';
|
||
|
|
||
|
char* pStart = chBuf;
|
||
|
while( *pStart )
|
||
|
{
|
||
|
char* pEnd;
|
||
|
|
||
|
// seek end-of-line
|
||
|
for(pEnd=pStart; *pEnd && *pEnd != '\r' && *pEnd != '\n'; ++pEnd)
|
||
|
;
|
||
|
if( *pEnd == '\0' )
|
||
|
break; // uncomplete message. stop parsing.
|
||
|
|
||
|
++nLinesProcessed;
|
||
|
|
||
|
// replace end-of-line with NULLs and skip
|
||
|
while( *pEnd == '\r' || *pEnd == '\n' )
|
||
|
*pEnd++ = '\0';
|
||
|
|
||
|
if( *pStart )
|
||
|
{
|
||
|
// process single message by monitor objects
|
||
|
CIrcMessage msg(pStart, true);
|
||
|
Notify(&msg);
|
||
|
}
|
||
|
|
||
|
cbInBuf -= pEnd - pStart;
|
||
|
assert(cbInBuf >= 0);
|
||
|
|
||
|
pStart = pEnd;
|
||
|
}
|
||
|
|
||
|
// discard processed messages
|
||
|
if( nLinesProcessed != 0 )
|
||
|
memmove(chBuf, pStart, cbInBuf+1);
|
||
|
}
|
||
|
|
||
|
if( m_socket )
|
||
|
m_socket.Close();
|
||
|
|
||
|
if( m_info.bIdentServer )
|
||
|
m_identServer.Stop();
|
||
|
|
||
|
// notify monitor objects that the connection has been closed
|
||
|
Notify(NULL);
|
||
|
}
|
||
|
|
||
|
DWORD WINAPI CIrcSession::ThreadProc(LPVOID pparam)
|
||
|
{
|
||
|
CIrcSession* pThis = (CIrcSession*)pparam;
|
||
|
try { pThis->DoReceive(); } catch( ... ) {}
|
||
|
pThis->m_info.Reset();
|
||
|
CloseHandle(pThis->m_hThread);
|
||
|
pThis->m_hThread = NULL;
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
void CIrcSession::AddMonitor(IIrcSessionMonitor* pMonitor)
|
||
|
{
|
||
|
assert(pMonitor != NULL);
|
||
|
EnterCriticalSection(&m_cs);
|
||
|
m_monitors.insert(pMonitor);
|
||
|
LeaveCriticalSection(&m_cs);
|
||
|
}
|
||
|
|
||
|
void CIrcSession::RemoveMonitor(IIrcSessionMonitor* pMonitor)
|
||
|
{
|
||
|
assert(pMonitor != NULL);
|
||
|
EnterCriticalSection(&m_cs);
|
||
|
m_monitors.erase(pMonitor);
|
||
|
LeaveCriticalSection(&m_cs);
|
||
|
}
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////
|
||
|
|
||
|
CIrcSessionInfo::CIrcSessionInfo()
|
||
|
: iPort(0), bIdentServer(false), iIdentServerPort(0)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
CIrcSessionInfo::CIrcSessionInfo(const CIrcSessionInfo& si)
|
||
|
: sServer(si.sServer),
|
||
|
sServerName(si.sServerName),
|
||
|
iPort(si.iPort),
|
||
|
sNick(si.sNick),
|
||
|
sUserID(si.sUserID),
|
||
|
sFullName(si.sFullName),
|
||
|
sPassword(si.sPassword),
|
||
|
bIdentServer(si.bIdentServer),
|
||
|
sIdentServerType(si.sIdentServerType),
|
||
|
iIdentServerPort(si.iIdentServerPort)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
void CIrcSessionInfo::Reset()
|
||
|
{
|
||
|
sServer = "";
|
||
|
sServerName = "";
|
||
|
iPort = 0;
|
||
|
sNick = "";
|
||
|
sUserID = "";
|
||
|
sFullName = "";
|
||
|
sPassword = "";
|
||
|
bIdentServer = false;
|
||
|
sIdentServerType = "";
|
||
|
iIdentServerPort = 0;
|
||
|
}
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////
|
||
|
|
||
|
CIrcIdentServer::CIrcIdentServer()
|
||
|
: m_uiPort(0), m_hThread(NULL)
|
||
|
{
|
||
|
m_hReady = CreateEvent(NULL,TRUE,FALSE,"IDENTEVENT");
|
||
|
}
|
||
|
|
||
|
CIrcIdentServer::~CIrcIdentServer()
|
||
|
{
|
||
|
CloseHandle(m_hReady);
|
||
|
Stop();
|
||
|
}
|
||
|
|
||
|
bool CIrcIdentServer::Start(
|
||
|
const char* lpszUserID,
|
||
|
unsigned int uiPort,
|
||
|
const char* lpszResponseType
|
||
|
)
|
||
|
{
|
||
|
if( m_socket )
|
||
|
return false;
|
||
|
|
||
|
if( !m_socket.Create() )
|
||
|
return false;
|
||
|
|
||
|
if( !m_socket.Bind(InetAddr((WORD)uiPort)) )
|
||
|
{
|
||
|
m_socket.Close();
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
m_sResponseType = lpszResponseType;
|
||
|
m_sUserID = lpszUserID;
|
||
|
m_uiPort = uiPort;
|
||
|
|
||
|
DWORD dwThreadId = 0;
|
||
|
m_hThread = CreateThread(NULL, 0, ListenProc, this, 0, &dwThreadId);
|
||
|
WaitForSingleObject(m_hReady,INFINITE);
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
void CIrcIdentServer::Stop()
|
||
|
{
|
||
|
if( m_hThread )
|
||
|
{
|
||
|
m_socket.Close();
|
||
|
Sleep(100);
|
||
|
if( WaitForSingleObject(m_hThread, 2000) != WAIT_OBJECT_0 && m_hThread )
|
||
|
{
|
||
|
TerminateThread(m_hThread, 1);
|
||
|
CloseHandle(m_hThread);
|
||
|
m_hThread = NULL;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void CIrcIdentServer::DoThread()
|
||
|
{
|
||
|
m_socket.Listen();
|
||
|
|
||
|
while( (bool)m_socket )
|
||
|
{
|
||
|
InetAddr addr;
|
||
|
|
||
|
SetEvent(m_hReady);
|
||
|
Socket s = m_socket.Accept(addr);
|
||
|
//int iErr = WSAGetLastError();
|
||
|
if( !(bool)s )
|
||
|
break;
|
||
|
|
||
|
char szBuf[1024];
|
||
|
int cbRead = s.Receive((unsigned char*)szBuf, sizeof(szBuf)-1);
|
||
|
if( cbRead <= 0 )
|
||
|
continue;
|
||
|
szBuf[cbRead] = '\0';
|
||
|
|
||
|
// strip CRLF from query
|
||
|
for(char* p = szBuf; *p && *p != '\r' && *p != '\n'; ++p)
|
||
|
;
|
||
|
*p = '\0';
|
||
|
|
||
|
s.Send("%s : USERID : %s : %s\r\n",
|
||
|
szBuf, m_sResponseType.c_str(), m_sUserID.c_str());
|
||
|
Sleep(500);
|
||
|
s.Close();
|
||
|
}
|
||
|
|
||
|
SetEvent(m_hReady);
|
||
|
|
||
|
m_socket.Close();
|
||
|
}
|
||
|
|
||
|
DWORD WINAPI CIrcIdentServer::ListenProc(LPVOID pparam)
|
||
|
{
|
||
|
CIrcIdentServer* pThis = (CIrcIdentServer*)pparam;
|
||
|
|
||
|
try { pThis->DoThread(); } catch( ... ) {}
|
||
|
|
||
|
pThis->m_sResponseType = "";
|
||
|
pThis->m_sUserID = "";
|
||
|
pThis->m_uiPort = 0;
|
||
|
|
||
|
CloseHandle(pThis->m_hThread);
|
||
|
pThis->m_hThread = NULL;
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////
|
||
|
|
||
|
CIrcMonitor::HandlersMap CIrcMonitor::m_handlers;
|
||
|
CIrcMonitor::IrcCommandsMapsListEntry CIrcMonitor::m_handlersMapsListEntry
|
||
|
= { &CIrcMonitor::m_handlers, NULL };
|
||
|
|
||
|
|
||
|
CIrcMonitor::CIrcMonitor(CIrcSession& session)
|
||
|
: m_session(session)
|
||
|
{
|
||
|
m_xPost.SetMonitor(this);
|
||
|
}
|
||
|
|
||
|
CIrcMonitor::~CIrcMonitor()
|
||
|
{
|
||
|
}
|
||
|
|
||
|
void CIrcMonitor::OnIrcMessage(const CIrcMessage* pmsg)
|
||
|
{
|
||
|
CIrcMessage* pMsgCopy = NULL;
|
||
|
if( pmsg )
|
||
|
pMsgCopy = new CIrcMessage(*pmsg);
|
||
|
m_xPost.Post(0, (LPARAM)pMsgCopy);
|
||
|
}
|
||
|
|
||
|
void CIrcMonitor::OnCrossThreadsMessage(WPARAM wParam, LPARAM lParam)
|
||
|
{
|
||
|
CIrcMessage* pmsg = (CIrcMessage*)lParam;
|
||
|
|
||
|
OnIrcAll(pmsg);
|
||
|
|
||
|
if( pmsg )
|
||
|
{
|
||
|
PfnIrcMessageHandler pfn = FindMethod(pmsg->sCommand.c_str());
|
||
|
if( pfn )
|
||
|
{
|
||
|
// call member function. if it returns 'false',
|
||
|
// call the default handling
|
||
|
if( !(this->*pfn)(pmsg) )
|
||
|
OnIrcDefault(pmsg);
|
||
|
}
|
||
|
else // handler not found. call default handler
|
||
|
OnIrcDefault(pmsg);
|
||
|
delete pmsg;
|
||
|
}
|
||
|
else
|
||
|
OnIrcDisconnected();
|
||
|
}
|
||
|
|
||
|
CIrcMonitor::PfnIrcMessageHandler CIrcMonitor::FindMethod(const char* lpszName)
|
||
|
{
|
||
|
// call the recursive version with the most derived map
|
||
|
return FindMethod(GetIrcCommandsMap(), lpszName);
|
||
|
}
|
||
|
|
||
|
CIrcMonitor::PfnIrcMessageHandler CIrcMonitor::FindMethod(IrcCommandsMapsListEntry* pMapsList, const char* lpszName)
|
||
|
{
|
||
|
HandlersMap::iterator it = pMapsList->pHandlersMap->find(lpszName);
|
||
|
if( it != pMapsList->pHandlersMap->end() )
|
||
|
return (*it).second; // found !
|
||
|
else if( pMapsList->pBaseHandlersMap )
|
||
|
return FindMethod(pMapsList->pBaseHandlersMap, lpszName); // try at base class
|
||
|
return NULL; // not found in any map
|
||
|
}
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////
|
||
|
|
||
|
DECLARE_IRC_MAP(CIrcDefaultMonitor, CIrcMonitor)
|
||
|
|
||
|
CIrcDefaultMonitor::CIrcDefaultMonitor(CIrcSession& session)
|
||
|
: CIrcMonitor(session)
|
||
|
{
|
||
|
IRC_MAP_ENTRY(CIrcDefaultMonitor, "NICK", OnIrc_NICK)
|
||
|
IRC_MAP_ENTRY(CIrcDefaultMonitor, "PING", OnIrc_PING)
|
||
|
IRC_MAP_ENTRY(CIrcDefaultMonitor, "002", OnIrc_YOURHOST)
|
||
|
IRC_MAP_ENTRY(CIrcDefaultMonitor, "005", OnIrc_BOUNCE)
|
||
|
}
|
||
|
|
||
|
bool CIrcDefaultMonitor::OnIrc_NICK(const CIrcMessage* pmsg)
|
||
|
{
|
||
|
if( (m_session.GetInfo().sNick == pmsg->prefix.sNick) && (pmsg->parameters.size() > 0) )
|
||
|
m_session.m_info.sNick = pmsg->parameters[0];
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
bool CIrcDefaultMonitor::OnIrc_PING(const CIrcMessage* pmsg)
|
||
|
{
|
||
|
char szResponse[100];
|
||
|
sprintf(szResponse, "PONG %s", pmsg->parameters[0].c_str());
|
||
|
m_session << CIrcMessage(szResponse);
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
bool CIrcDefaultMonitor::OnIrc_YOURHOST(const CIrcMessage* pmsg)
|
||
|
{
|
||
|
static const char* lpszFmt = "Your host is %[^ \x5b,], running version %s";
|
||
|
char szHostName[100], szVersion[100];
|
||
|
if( sscanf(pmsg->parameters[1].c_str(), lpszFmt, &szHostName, &szVersion) > 0 )
|
||
|
m_session.m_info.sServerName = szHostName;
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
bool CIrcDefaultMonitor::OnIrc_BOUNCE(const CIrcMessage* pmsg)
|
||
|
{
|
||
|
static const char* lpszFmt = "Try server %[^ ,], port %d";
|
||
|
char szAltServer[100];
|
||
|
int iAltPort = 0;
|
||
|
if( sscanf(pmsg->parameters[1].c_str(), lpszFmt, &szAltServer, &iAltPort) == 2 )
|
||
|
{
|
||
|
}
|
||
|
return false;
|
||
|
}
|