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.
3349 lines
113 KiB
3349 lines
113 KiB
// -*- mode: c++; c-file-style: "bsd"; c-basic-offset: 4; tabs-width: 4; indent-tabs-mode: nil -*-
|
|
|
|
/*
|
|
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.
|
|
*/
|
|
|
|
/*
|
|
Copyright (C) 2002 Dario Abatianni <eisfuchs@tigress.com>
|
|
Copyright (C) 2005 Ismail Donmez <ismail@kde.org>
|
|
Copyright (C) 2005-2006 Peter Simonsson <psn@linux.se>
|
|
Copyright (C) 2006-2008 Eli J. MacKenzie <argonel at gmail.com>
|
|
Copyright (C) 2005-2008 Eike Hein <hein@kde.org>
|
|
*/
|
|
|
|
#include "server.h"
|
|
#include "ircqueue.h"
|
|
#include "query.h"
|
|
#include "channel.h"
|
|
#include "konversationapplication.h"
|
|
#include "connectionmanager.h"
|
|
#include "dcccommon.h"
|
|
#include "dcctransferpanel.h"
|
|
#include "dcctransferpanelitem.h"
|
|
#include "dcctransfersend.h"
|
|
#include "dcctransferrecv.h"
|
|
#include "dccrecipientdialog.h"
|
|
#include "nick.h"
|
|
#include "irccharsets.h"
|
|
#include "viewcontainer.h"
|
|
#include "statuspanel.h"
|
|
#include "rawlog.h"
|
|
#include "channellistpanel.h"
|
|
#include "scriptlauncher.h"
|
|
#include "servergroupsettings.h"
|
|
#include "addressbook.h"
|
|
#include "serverison.h"
|
|
#include "common.h"
|
|
#include "notificationhandler.h"
|
|
#include "blowfish.h"
|
|
#include "dcctransfermanager.h"
|
|
|
|
#include <tqregexp.h>
|
|
#include <tqhostaddress.h>
|
|
#include <tqtextcodec.h>
|
|
#include <tqdatetime.h>
|
|
|
|
#include <kapplication.h>
|
|
#include <klocale.h>
|
|
#include <kdebug.h>
|
|
#include <tdefiledialog.h>
|
|
#include <kinputdialog.h>
|
|
#include <kmessagebox.h>
|
|
#include <kresolver.h>
|
|
#include <tdesocketdevice.h>
|
|
#include <kaction.h>
|
|
#include <kstringhandler.h>
|
|
#include <tdeversion.h>
|
|
#include <twin.h>
|
|
#include <config.h>
|
|
|
|
|
|
int Server::m_availableConnectionId = 0;
|
|
|
|
Server::Server(TQObject* parent, ConnectionSettings& settings) : TQObject(parent)
|
|
{
|
|
m_connectionId = m_availableConnectionId;
|
|
m_availableConnectionId++;
|
|
|
|
setConnectionSettings(settings);
|
|
|
|
m_connectionState = Konversation::SSNeverConnected;
|
|
|
|
for (int i=0;i<=_max_queue();i++)
|
|
{
|
|
TQValueList<int> r=Preferences::queueRate(i);
|
|
IRCQueue *q=new IRCQueue(this, staticrates[i]); //FIXME these are supposed to be in the rc
|
|
m_queues.append(q);
|
|
}
|
|
|
|
m_processingIncoming = false;
|
|
m_identifyMsg = false;
|
|
m_autoIdentifyLock = false;
|
|
m_autoJoin = false;
|
|
|
|
m_nickIndices.clear();
|
|
m_nickIndices.append(0);
|
|
|
|
m_currentLag = -1;
|
|
m_rawLog = 0;
|
|
m_channelListPanel = 0;
|
|
m_serverISON = 0;
|
|
m_away = false;
|
|
m_socket = 0;
|
|
m_prevISONList = TQStringList();
|
|
m_bytesReceived = 0;
|
|
m_encodedBytesSent=0;
|
|
m_bytesSent=0;
|
|
m_linesSent=0;
|
|
// TODO fold these into a TQMAP, and these need to be reset to RFC values if this server object is reused.
|
|
m_serverNickPrefixModes = "ovh";
|
|
m_serverNickPrefixes = "@+%";
|
|
m_channelPrefixes = "#&";
|
|
|
|
setName(TQString("server_" + settings.name()).ascii());
|
|
|
|
setNickname(settings.initialNick());
|
|
obtainNickInfo(getNickname());
|
|
|
|
m_statusView = getViewContainer()->addStatusView(this);
|
|
|
|
if (Preferences::rawLog())
|
|
addRawLog(false);
|
|
|
|
m_inputFilter.setServer(this);
|
|
m_outputFilter = new Konversation::OutputFilter(this);
|
|
m_scriptLauncher = new ScriptLauncher(this);
|
|
|
|
// don't delete items when they are removed
|
|
m_channelList.setAutoDelete(false);
|
|
// For /msg query completion
|
|
m_completeQueryPosition = 0;
|
|
|
|
updateAutoJoin(settings.initialChannel());
|
|
|
|
if (!getIdentity()->getShellCommand().isEmpty())
|
|
TQTimer::singleShot(0, this, TQT_SLOT(doPreShellCommand()));
|
|
else
|
|
TQTimer::singleShot(0, this, TQT_SLOT(connectToIRCServer()));
|
|
|
|
initTimers();
|
|
|
|
if (getIdentity()->getShellCommand().isEmpty())
|
|
connectSignals();
|
|
}
|
|
|
|
Server::~Server()
|
|
{
|
|
//send queued messages
|
|
kdDebug() << "Server::~Server(" << getServerName() << ")" << endl;
|
|
|
|
// Delete helper object.
|
|
delete m_serverISON;
|
|
m_serverISON = 0;
|
|
|
|
// clear nicks online
|
|
emit nicksNowOnline(this,TQStringList(),true);
|
|
|
|
// Make sure no signals get sent to a soon to be dying Server Window
|
|
if (m_socket)
|
|
{
|
|
m_socket->blockSignals(true);
|
|
m_socket->deleteLater();
|
|
}
|
|
|
|
if (m_statusView) delete m_statusView;
|
|
|
|
closeRawLog();
|
|
closeChannelListPanel();
|
|
|
|
m_channelList.setAutoDelete(true);
|
|
m_channelList.clear();
|
|
|
|
m_queryList.setAutoDelete(true);
|
|
m_queryList.clear();
|
|
|
|
// Delete all the NickInfos and ChannelNick structures.
|
|
m_allNicks.clear();
|
|
|
|
ChannelMembershipMap::ConstIterator it;
|
|
|
|
for ( it = m_joinedChannels.begin(); it != m_joinedChannels.end(); ++it )
|
|
delete it.data();
|
|
m_joinedChannels.clear();
|
|
|
|
for ( it = m_unjoinedChannels.begin(); it != m_unjoinedChannels.end(); ++it )
|
|
delete it.data();
|
|
m_unjoinedChannels.clear();
|
|
|
|
m_queryNicks.clear();
|
|
|
|
//Delete the queues
|
|
for (TQValueVector<IRCQueue *>::iterator it=m_queues.begin(); it != m_queues.end(); ++it)
|
|
delete *it;
|
|
|
|
emit destroyed(m_connectionId);
|
|
|
|
kdDebug() << "~Server done" << endl;
|
|
}
|
|
|
|
//... so called to match the ChatWindow derivatives.
|
|
bool Server::closeYourself(bool)
|
|
{
|
|
TQTimer::singleShot(0, m_statusView, TQT_SLOT(serverSaysClose()));
|
|
return true;
|
|
}
|
|
|
|
void Server::doPreShellCommand()
|
|
{
|
|
|
|
TQString command = getIdentity()->getShellCommand();
|
|
getStatusView()->appendServerMessage(i18n("Info"),"Running preconfigured command...");
|
|
|
|
connect(&m_preShellCommand,TQT_SIGNAL(processExited(TDEProcess*)), this, TQT_SLOT(preShellCommandExited(TDEProcess*)));
|
|
|
|
TQStringList commandList = TQStringList::split(" ",command);
|
|
|
|
for (TQStringList::ConstIterator it = commandList.begin(); it != commandList.end(); ++it)
|
|
m_preShellCommand << *it;
|
|
|
|
if (!m_preShellCommand.start()) preShellCommandExited(NULL);
|
|
}
|
|
|
|
void Server::_fetchRates()
|
|
{
|
|
for (int i=0;i<=_max_queue();i++)
|
|
{
|
|
TQValueList<int> r=Preferences::queueRate(i);
|
|
staticrates[i]=IRCQueue::EmptyingRate(r[0], r[1]*1000,IRCQueue::EmptyingRate::RateType(r[2]));
|
|
}
|
|
}
|
|
|
|
void Server::_stashRates()
|
|
{
|
|
for (int i=0;i<=_max_queue();i++)
|
|
{
|
|
TQValueList<int> r;
|
|
r.append(staticrates[i].m_rate);
|
|
r.append(staticrates[i].m_interval/1000);
|
|
r.append(int(staticrates[i].m_type));
|
|
Preferences::setQueueRate(i, r);
|
|
}
|
|
}
|
|
|
|
void Server::_resetRates()
|
|
{
|
|
for (int i=0;i<=_max_queue();i++)
|
|
{
|
|
Preferences::self()->queueRateItem(i)->setDefault();
|
|
TQValueList<int> r=Preferences::queueRate(i);
|
|
staticrates[i]=IRCQueue::EmptyingRate(r[0], r[1]*1000,IRCQueue::EmptyingRate::RateType(r[2]));
|
|
}
|
|
}
|
|
|
|
void Server::initTimers()
|
|
{
|
|
m_notifyTimer.setName("notify_timer");
|
|
m_incomingTimer.setName("incoming_timer");
|
|
}
|
|
|
|
void Server::connectSignals()
|
|
{
|
|
// Timers
|
|
connect(&m_incomingTimer, TQT_SIGNAL(timeout()), this, TQT_SLOT(processIncomingData()));
|
|
connect(&m_notifyTimer, TQT_SIGNAL(timeout()), this, TQT_SLOT(notifyTimeout()));
|
|
connect(&m_pingResponseTimer, TQT_SIGNAL(timeout()), this, TQT_SLOT(updateLongPongLag()));
|
|
|
|
// OutputFilter
|
|
connect(getOutputFilter(), TQT_SIGNAL(requestDccSend()), this,TQT_SLOT(requestDccSend()));
|
|
connect(getOutputFilter(), TQT_SIGNAL(requestDccSend(const TQString&)), this, TQT_SLOT(requestDccSend(const TQString&)));
|
|
connect(getOutputFilter(), TQT_SIGNAL(multiServerCommand(const TQString&, const TQString&)),
|
|
this, TQT_SLOT(sendMultiServerCommand(const TQString&, const TQString&)));
|
|
connect(getOutputFilter(), TQT_SIGNAL(reconnectServer()), this, TQT_SLOT(reconnect()));
|
|
connect(getOutputFilter(), TQT_SIGNAL(disconnectServer()), this, TQT_SLOT(disconnect()));
|
|
connect(getOutputFilter(), TQT_SIGNAL(openDccSend(const TQString &, KURL)), this, TQT_SLOT(addDccSend(const TQString &, KURL)));
|
|
connect(getOutputFilter(), TQT_SIGNAL(openDccChat(const TQString &)), this, TQT_SLOT(openDccChat(const TQString &)));
|
|
connect(getOutputFilter(), TQT_SIGNAL(sendToAllChannels(const TQString&)), this, TQT_SLOT(sendToAllChannels(const TQString&)));
|
|
connect(getOutputFilter(), TQT_SIGNAL(banUsers(const TQStringList&,const TQString&,const TQString&)),
|
|
this, TQT_SLOT(requestBan(const TQStringList&,const TQString&,const TQString&)));
|
|
connect(getOutputFilter(), TQT_SIGNAL(unbanUsers(const TQString&,const TQString&)),
|
|
this, TQT_SLOT(requestUnban(const TQString&,const TQString&)));
|
|
connect(getOutputFilter(), TQT_SIGNAL(openRawLog(bool)), this, TQT_SLOT(addRawLog(bool)));
|
|
connect(getOutputFilter(), TQT_SIGNAL(closeRawLog()), this, TQT_SLOT(closeRawLog()));
|
|
connect(getOutputFilter(), TQT_SIGNAL(encodingChanged()), this, TQT_SLOT(updateEncoding()));
|
|
|
|
KonversationApplication* konvApp = static_cast<KonversationApplication*>(kapp);
|
|
connect(getOutputFilter(), TQT_SIGNAL(connectTo(Konversation::ConnectionFlag, const TQString&,
|
|
const TQString&, const TQString&, const TQString&, const TQString&, bool)),
|
|
konvApp->getConnectionManager(), TQT_SLOT(connectTo(Konversation::ConnectionFlag,
|
|
const TQString&, const TQString&, const TQString&, const TQString&, const TQString&, bool)));
|
|
|
|
connect(konvApp->getDccTransferManager(), TQT_SIGNAL(newTransferQueued(DccTransfer*)),
|
|
this, TQT_SLOT(slotNewDccTransferItemQueued(DccTransfer*)));
|
|
|
|
connect(konvApp, TQT_SIGNAL(appearanceChanged()), this, TQT_SLOT(startNotifyTimer()));
|
|
|
|
// ViewContainer
|
|
connect(this, TQT_SIGNAL(showView(ChatWindow*)), getViewContainer(), TQT_SLOT(showView(ChatWindow*)));
|
|
connect(this, TQT_SIGNAL(addDccPanel()), getViewContainer(), TQT_SLOT(addDccPanel()));
|
|
connect(this, TQT_SIGNAL(addDccChat(const TQString&,const TQString&,const TQStringList&,bool)),
|
|
getViewContainer(), TQT_SLOT(addDccChat(const TQString&,const TQString&,const TQStringList&,bool)) );
|
|
connect(this, TQT_SIGNAL(serverLag(Server*, int)), getViewContainer(), TQT_SIGNAL(updateStatusBarLagLabel(Server*, int)));
|
|
connect(this, TQT_SIGNAL(tooLongLag(Server*, int)), getViewContainer(), TQT_SIGNAL(setStatusBarLagLabelTooLongLag(Server*, int)));
|
|
connect(this, TQT_SIGNAL(resetLag()), getViewContainer(), TQT_SIGNAL(resetStatusBarLagLabel()));
|
|
connect(getOutputFilter(), TQT_SIGNAL(showView(ChatWindow*)), getViewContainer(), TQT_SLOT(showView(ChatWindow*)));
|
|
connect(getOutputFilter(), TQT_SIGNAL(openKonsolePanel()), getViewContainer(), TQT_SLOT(addKonsolePanel()));
|
|
connect(getOutputFilter(), TQT_SIGNAL(openChannelList(const TQString&, bool)), getViewContainer(), TQT_SLOT(openChannelList(const TQString&, bool)));
|
|
connect(getOutputFilter(), TQT_SIGNAL(closeDccPanel()), getViewContainer(), TQT_SLOT(closeDccPanel()));
|
|
connect(getOutputFilter(), TQT_SIGNAL(addDccPanel()), getViewContainer(), TQT_SLOT(addDccPanel()));
|
|
connect(&m_inputFilter, TQT_SIGNAL(addDccChat(const TQString&,const TQString&,const TQStringList&,bool)),
|
|
getViewContainer(), TQT_SLOT(addDccChat(const TQString&,const TQString&,const TQStringList&,bool)) );
|
|
|
|
// Inputfilter
|
|
connect(&m_inputFilter, TQT_SIGNAL(welcome(const TQString&)), this, TQT_SLOT(connectionEstablished(const TQString&)));
|
|
connect(&m_inputFilter, TQT_SIGNAL(notifyResponse(const TQString&)), this, TQT_SLOT(notifyResponse(const TQString&)));
|
|
connect(&m_inputFilter, TQT_SIGNAL(startReverseDccSendTransfer(const TQString&,const TQStringList&)),
|
|
this, TQT_SLOT(startReverseDccSendTransfer(const TQString&,const TQStringList&)));
|
|
connect(&m_inputFilter, TQT_SIGNAL(addDccGet(const TQString&, const TQStringList&)),
|
|
this, TQT_SLOT(addDccGet(const TQString&, const TQStringList&)));
|
|
connect(&m_inputFilter, TQT_SIGNAL(resumeDccGetTransfer(const TQString&, const TQStringList&)),
|
|
this, TQT_SLOT(resumeDccGetTransfer(const TQString&, const TQStringList&)));
|
|
connect(&m_inputFilter, TQT_SIGNAL(resumeDccSendTransfer(const TQString&, const TQStringList&)),
|
|
this, TQT_SLOT(resumeDccSendTransfer(const TQString&, const TQStringList&)));
|
|
connect(&m_inputFilter, TQT_SIGNAL(userhost(const TQString&,const TQString&,bool,bool)),
|
|
this, TQT_SLOT(userhost(const TQString&,const TQString&,bool,bool)) );
|
|
connect(&m_inputFilter, TQT_SIGNAL(topicAuthor(const TQString&,const TQString&,TQDateTime)),
|
|
this, TQT_SLOT(setTopicAuthor(const TQString&,const TQString&,TQDateTime)) );
|
|
connect(&m_inputFilter, TQT_SIGNAL(endOfWho(const TQString&)),
|
|
this, TQT_SLOT(endOfWho(const TQString&)) );
|
|
connect(&m_inputFilter, TQT_SIGNAL(invitation(const TQString&,const TQString&)),
|
|
this,TQT_SLOT(invitation(const TQString&,const TQString&)) );
|
|
connect(&m_inputFilter, TQT_SIGNAL(addToChannelList(const TQString&, int, const TQString& )),
|
|
this, TQT_SLOT(addToChannelList(const TQString&, int, const TQString& )));
|
|
|
|
// Status View
|
|
connect(this, TQT_SIGNAL(serverOnline(bool)), getStatusView(), TQT_SLOT(serverOnline(bool)));
|
|
|
|
// Scripts
|
|
connect(getOutputFilter(), TQT_SIGNAL(launchScript(const TQString&, const TQString&)),
|
|
m_scriptLauncher, TQT_SLOT(launchScript(const TQString&, const TQString&)));
|
|
connect(m_scriptLauncher, TQT_SIGNAL(scriptNotFound(const TQString&)),
|
|
this, TQT_SLOT(scriptNotFound(const TQString&)));
|
|
connect(m_scriptLauncher, TQT_SIGNAL(scriptExecutionError(const TQString&)),
|
|
this, TQT_SLOT(scriptExecutionError(const TQString&)));
|
|
|
|
// Stats
|
|
connect(this, TQT_SIGNAL(sentStat(int, int)), TQT_SLOT(collectStats(int, int)));
|
|
}
|
|
|
|
int Server::getPort()
|
|
{
|
|
return getConnectionSettings().server().port();
|
|
}
|
|
|
|
int Server::getLag() const
|
|
{
|
|
return m_currentLag;
|
|
}
|
|
|
|
bool Server::getAutoJoin() const
|
|
{
|
|
return m_autoJoin;
|
|
}
|
|
|
|
void Server::setAutoJoin(bool on)
|
|
{
|
|
m_autoJoin = on;
|
|
}
|
|
|
|
void Server::preShellCommandExited(TDEProcess* proc)
|
|
{
|
|
|
|
if (proc && proc->normalExit())
|
|
getStatusView()->appendServerMessage(i18n("Info"),"Process executed successfully!");
|
|
else
|
|
getStatusView()->appendServerMessage(i18n("Warning"),"There was a problem while executing the command!");
|
|
|
|
connectToIRCServer();
|
|
connectSignals();
|
|
}
|
|
|
|
void Server::connectToIRCServer()
|
|
{
|
|
if (!isConnected())
|
|
{
|
|
updateConnectionState(Konversation::SSConnecting);
|
|
|
|
m_autoIdentifyLock = false;
|
|
m_ownIpByUserhost = TQString();
|
|
|
|
resetQueues();
|
|
|
|
// This is needed to support server groups with mixed SSL and nonSSL servers
|
|
delete m_socket;
|
|
m_socket = 0;
|
|
resetNickSelection();
|
|
|
|
// connect() will do a async lookup too
|
|
if(!getConnectionSettings().server().SSLEnabled())
|
|
{
|
|
m_socket = new KNetwork::TDEBufferedSocket(TQString(), TQString(), 0L, "serverSocket");
|
|
connect(m_socket, TQT_SIGNAL(connected(const KResolverEntry&)), TQT_SLOT (ircServerConnectionSuccess()));
|
|
}
|
|
else
|
|
{
|
|
m_socket = new SSLSocket(getViewContainer()->getWindow(), 0L, "serverSSLSocket");
|
|
connect(m_socket, TQT_SIGNAL(sslInitDone()), TQT_SLOT(ircServerConnectionSuccess()));
|
|
connect(m_socket, TQT_SIGNAL(sslFailure(const TQString&)), TQT_SIGNAL(sslInitFailure()));
|
|
connect(m_socket, TQT_SIGNAL(sslFailure(const TQString&)), TQT_SLOT(sslError(const TQString&)));
|
|
}
|
|
|
|
m_socket->enableWrite(false);
|
|
|
|
connect(m_socket, TQT_SIGNAL(hostFound()), TQT_SLOT(lookupFinished()));
|
|
connect(m_socket, TQT_SIGNAL(gotError(int)), TQT_SLOT(broken(int)) );
|
|
connect(m_socket, TQT_SIGNAL(readyRead()), TQT_SLOT(incoming()));
|
|
connect(m_socket, TQT_SIGNAL(closed()), TQT_SLOT(closed()));
|
|
|
|
m_socket->connect(getConnectionSettings().server().host(), TQString::number(getConnectionSettings().server().port()));
|
|
|
|
// set up the connection details
|
|
setPrefixes(m_serverNickPrefixModes, m_serverNickPrefixes);
|
|
getStatusView()->appendServerMessage(i18n("Info"),i18n("Looking for server %1:%2...")
|
|
.arg(getConnectionSettings().server().host())
|
|
.arg(getConnectionSettings().server().port()));
|
|
// reset InputFilter (auto request info, /WHO request info)
|
|
m_inputFilter.reset();
|
|
}
|
|
else
|
|
kdDebug() << "connectToIRCServer() called while already connected: This should never happen." << endl;
|
|
}
|
|
|
|
void Server::showSSLDialog()
|
|
{
|
|
SSLSocket* sslsocket = dynamic_cast<SSLSocket*>(m_socket);
|
|
|
|
if (sslsocket) sslsocket->showInfoDialog();
|
|
}
|
|
|
|
// set available channel types according to 005 RPL_ISUPPORT
|
|
void Server::setChannelTypes(const TQString &pre)
|
|
{
|
|
m_channelPrefixes = pre;
|
|
}
|
|
|
|
TQString Server::getChannelTypes() const
|
|
{
|
|
return m_channelPrefixes;
|
|
}
|
|
|
|
// set user mode prefixes according to non-standard 005-Reply (see inputfilter.cpp)
|
|
void Server::setPrefixes(const TQString &modes, const TQString& prefixes)
|
|
{
|
|
// NOTE: serverModes is TQString(), if server did not supply the
|
|
// modes which relates to the network's nick-prefixes
|
|
m_serverNickPrefixModes = modes;
|
|
m_serverNickPrefixes = prefixes;
|
|
}
|
|
|
|
// return a nickname without possible mode character at the beginning
|
|
void Server::mangleNicknameWithModes(TQString& nickname,bool& isAdmin,bool& isOwner,
|
|
bool& isOp,bool& isHalfop,bool& hasVoice)
|
|
{
|
|
isAdmin = false;
|
|
isOwner = false;
|
|
isOp = false;
|
|
isHalfop = false;
|
|
hasVoice = false;
|
|
|
|
int modeIndex;
|
|
|
|
if (nickname.isEmpty()) return;
|
|
|
|
while ((modeIndex = m_serverNickPrefixes.find(nickname[0])) != -1)
|
|
{
|
|
if(nickname.isEmpty())
|
|
return;
|
|
nickname = nickname.mid(1);
|
|
// cut off the prefix
|
|
bool recognisedMode = false;
|
|
// determine, whether status is like op or like voice
|
|
while((modeIndex)<int(m_serverNickPrefixes.length()) && !recognisedMode)
|
|
{
|
|
switch(m_serverNickPrefixes[modeIndex].latin1())
|
|
{
|
|
case '*': // admin (EUIRC)
|
|
{
|
|
isAdmin = true;
|
|
recognisedMode = true;
|
|
break;
|
|
}
|
|
case '&': // admin (unrealircd)
|
|
{
|
|
isAdmin = true;
|
|
recognisedMode = true;
|
|
break;
|
|
}
|
|
case '!': // channel owner (RFC2811)
|
|
{
|
|
isOwner = true;
|
|
recognisedMode = true;
|
|
break;
|
|
}
|
|
case '~': // channel owner (unrealircd)
|
|
{
|
|
isOwner = true;
|
|
recognisedMode = true;
|
|
break;
|
|
}
|
|
case '@': // channel operator (RFC1459)
|
|
{
|
|
isOp = true;
|
|
recognisedMode = true;
|
|
break;
|
|
}
|
|
case '%': // halfop
|
|
{
|
|
isHalfop = true;
|
|
recognisedMode = true;
|
|
break;
|
|
}
|
|
case '+': // voiced (RFC1459)
|
|
{
|
|
hasVoice = true;
|
|
recognisedMode = true;
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
++modeIndex;
|
|
break;
|
|
}
|
|
} //switch to recognise the mode.
|
|
} // loop through the modes to find one recognised
|
|
} // loop through the name
|
|
}
|
|
|
|
void Server::lookupFinished()
|
|
{
|
|
// error during lookup
|
|
if(m_socket->status())
|
|
{
|
|
// inform user about the error
|
|
getStatusView()->appendServerMessage(i18n("Error"),i18n("Server %1 not found: %2")
|
|
.arg(getConnectionSettings().server().host())
|
|
.arg(m_socket->TDESocketBase::errorString(m_socket->error())));
|
|
|
|
m_socket->resetStatus();
|
|
|
|
// broken connection
|
|
broken(m_socket->error());
|
|
}
|
|
else
|
|
getStatusView()->appendServerMessage(i18n("Info"),i18n("Server found, connecting..."));
|
|
}
|
|
|
|
void Server::ircServerConnectionSuccess()
|
|
{
|
|
getConnectionSettings().setReconnectCount(0);
|
|
|
|
Konversation::ServerSettings serverSettings = getConnectionSettings().server();
|
|
|
|
connect(this, TQT_SIGNAL(nicknameChanged(const TQString&)), getStatusView(), TQT_SLOT(setNickname(const TQString&)));
|
|
getStatusView()->appendServerMessage(i18n("Info"),i18n("Connected; logging in..."));
|
|
|
|
TQString connectString = "USER " +
|
|
getIdentity()->getIdent() +
|
|
" 8 * :" + // 8 = +i; 4 = +w
|
|
getIdentity()->getRealName();
|
|
|
|
TQStringList ql;
|
|
if (!serverSettings.password().isEmpty())
|
|
ql << "PASS " + serverSettings.password();
|
|
|
|
ql << "NICK "+getNickname();
|
|
ql << connectString;
|
|
queueList(ql, HighPriority);
|
|
|
|
emit nicknameChanged(getNickname());
|
|
|
|
m_socket->enableRead(true);
|
|
}
|
|
|
|
void Server::broken(int state)
|
|
{
|
|
kdDebug() << "Connection broken (Socket fd " << m_socket->socketDevice()->socket() << ") " << state << "!" << endl;
|
|
|
|
m_socket->enableRead(false);
|
|
m_socket->enableWrite(false); //FIXME if we rely on this signal, it should be turned back on somewhere...
|
|
m_socket->blockSignals(true);
|
|
|
|
resetQueues();
|
|
|
|
m_notifyTimer.stop();
|
|
m_pingResponseTimer.stop();
|
|
m_inputFilter.setLagMeasuring(false);
|
|
m_currentLag = -1;
|
|
|
|
// HACK Only show one nick change dialog at connection time
|
|
if (getStatusView())
|
|
{
|
|
KDialogBase* nickChangeDialog = dynamic_cast<KDialogBase*>(
|
|
getStatusView()->child("NickChangeDialog", "KInputDialog"));
|
|
|
|
if (nickChangeDialog) nickChangeDialog->cancel();
|
|
}
|
|
|
|
emit resetLag();
|
|
emit nicksNowOnline(this,TQStringList(),true);
|
|
|
|
updateAutoJoin();
|
|
|
|
if (getConnectionState() != Konversation::SSDeliberatelyDisconnected)
|
|
{
|
|
static_cast<KonversationApplication*>(kapp)->notificationHandler()->connectionFailure(getStatusView(), getServerName());
|
|
|
|
TQString error = i18n("Connection to Server %1 lost: %2.")
|
|
.arg(getConnectionSettings().server().host())
|
|
.arg(KNetwork::TDESocketBase::errorString((KNetwork::TDESocketBase::SocketError)state));
|
|
|
|
getStatusView()->appendServerMessage(i18n("Error"), error);
|
|
|
|
updateConnectionState(Konversation::SSInvoluntarilyDisconnected);
|
|
}
|
|
}
|
|
|
|
void Server::sslError(const TQString& reason)
|
|
{
|
|
TQString error = i18n("Could not connect to %1:%2 using SSL encryption.Maybe the server does not support SSL, or perhaps you have the wrong port? %3")
|
|
.arg(getConnectionSettings().server().host())
|
|
.arg(getConnectionSettings().server().port())
|
|
.arg(reason);
|
|
getStatusView()->appendServerMessage(i18n("SSL Connection Error"),error);
|
|
|
|
updateConnectionState(Konversation::SSDeliberatelyDisconnected);
|
|
}
|
|
|
|
// Will be called from InputFilter as soon as the Welcome message was received
|
|
void Server::connectionEstablished(const TQString& ownHost)
|
|
{
|
|
// Some servers don't include the userhost in RPL_WELCOME, so we
|
|
// need to use RPL_USERHOST to get ahold of our IP later on
|
|
if (!ownHost.isEmpty())
|
|
KNetwork::KResolver::resolveAsync(this,TQT_SLOT(gotOwnResolvedHostByWelcome(KResolverResults)),ownHost,"0");
|
|
|
|
updateConnectionState(Konversation::SSConnected);
|
|
|
|
// Make a helper object to build ISON (notify) list and map offline nicks to addressbook.
|
|
// TODO: Give the object a kick to get it started?
|
|
m_serverISON = new ServerISON(this);
|
|
// get first notify very early
|
|
startNotifyTimer(1000);
|
|
// Register with services
|
|
registerWithServices();
|
|
// get own ip by userhost
|
|
requestUserhost(getNickname());
|
|
|
|
// Start the PINGPONG match
|
|
TQTimer::singleShot(1000 /*1 sec*/, this, TQT_SLOT(sendPing()));
|
|
|
|
// Recreate away state if we were set away prior to a reconnect.
|
|
if (m_away)
|
|
{
|
|
// Correct server's beliefs about its away state.
|
|
m_away = false;
|
|
requestAway(m_awayReason);
|
|
}
|
|
}
|
|
|
|
void Server::registerWithServices()
|
|
{
|
|
if (getIdentity() && !getIdentity()->getBot().isEmpty()
|
|
&& !getIdentity()->getPassword().isEmpty()
|
|
&& !m_autoIdentifyLock)
|
|
{
|
|
queue("PRIVMSG "+getIdentity()->getBot()+" :identify "+getIdentity()->getPassword(), HighPriority);
|
|
|
|
m_autoIdentifyLock = true;
|
|
}
|
|
}
|
|
|
|
//FIXME operator[] inserts an empty T& so each destination might just as well have its own key storage
|
|
TQCString Server::getKeyForRecipient(const TQString& recipient) const
|
|
{
|
|
return m_keyMap[recipient];
|
|
}
|
|
|
|
void Server::setKeyForRecipient(const TQString& recipient, const TQCString& key)
|
|
{
|
|
m_keyMap[recipient] = key;
|
|
}
|
|
|
|
void Server::gotOwnResolvedHostByWelcome(KResolverResults res)
|
|
{
|
|
if (res.error() == KResolver::NoError && !res.isEmpty())
|
|
m_ownIpByWelcome = res.first().address().nodeName();
|
|
else
|
|
kdDebug() << "Server::gotOwnResolvedHostByWelcome(): Got error: " << ( int )res.error() << endl;
|
|
}
|
|
|
|
void Server::quitServer()
|
|
{
|
|
// Make clear this is deliberate even if the QUIT never actually goes through the queue
|
|
// (i.e. this is not redundant with _send_internal()'s updateConnectionState() call for
|
|
// a QUIT).
|
|
updateConnectionState(Konversation::SSDeliberatelyDisconnected);
|
|
|
|
TQString command(Preferences::commandChar()+"QUIT");
|
|
Konversation::OutputFilterResult result = getOutputFilter()->parse(getNickname(),command, TQString());
|
|
queue(result.toServer, HighPriority);
|
|
|
|
m_socket->enableRead(false);
|
|
|
|
flushQueues();
|
|
|
|
m_socket->close();
|
|
|
|
getStatusView()->appendServerMessage(i18n("Info"), i18n("Disconnected from %1.").arg(getConnectionSettings().server().host()));
|
|
}
|
|
|
|
void Server::notifyAction(const TQString& nick)
|
|
{
|
|
// parse wildcards (toParse,nickname,channelName,nickList,parameter)
|
|
TQString out = parseWildcards(Preferences::notifyDoubleClickAction(),
|
|
getNickname(),
|
|
TQString(),
|
|
TQString(),
|
|
nick,
|
|
TQString());
|
|
|
|
// Send all strings, one after another
|
|
TQStringList outList = TQStringList::split('\n',out);
|
|
for (unsigned int index=0; index<outList.count(); ++index)
|
|
{
|
|
Konversation::OutputFilterResult result = getOutputFilter()->parse(getNickname(),outList[index],TQString());
|
|
queue(result.toServer);
|
|
} // endfor
|
|
}
|
|
|
|
void Server::notifyResponse(const TQString& nicksOnline)
|
|
{
|
|
bool nicksOnlineChanged = false;
|
|
TQStringList actualList = TQStringList::split(' ',nicksOnline);
|
|
TQString lcActual = ' ' + nicksOnline + ' ';
|
|
TQString lcPrevISON = ' ' + (m_prevISONList.join(" ")) + ' ';
|
|
|
|
TQStringList::iterator it;
|
|
|
|
//Are any nicks gone offline
|
|
for (it = m_prevISONList.begin(); it != m_prevISONList.end(); ++it)
|
|
{
|
|
if (lcActual.find(' ' + (*it) + ' ', 0, false) == -1)
|
|
{
|
|
setNickOffline(*it);
|
|
nicksOnlineChanged = true;
|
|
}
|
|
}
|
|
|
|
//Are any nicks gone online
|
|
for (it = actualList.begin(); it != actualList.end(); ++it)
|
|
{
|
|
if (lcPrevISON.find(' ' + (*it) + ' ', 0, false) == -1) {
|
|
setWatchedNickOnline(*it);
|
|
nicksOnlineChanged = true;
|
|
}
|
|
}
|
|
|
|
// Note: The list emitted in this signal *does* include nicks in joined channels.
|
|
emit nicksNowOnline(this, actualList, nicksOnlineChanged);
|
|
|
|
m_prevISONList = actualList;
|
|
|
|
// Next round
|
|
startNotifyTimer();
|
|
}
|
|
|
|
void Server::startNotifyTimer(int msec)
|
|
{
|
|
// make sure the timer gets started properly in case we have reconnected
|
|
m_notifyTimer.stop();
|
|
|
|
if (msec == 0) msec = Preferences::notifyDelay()*1000;
|
|
|
|
// start the timer in one shot mode
|
|
if (Preferences::useNotify())
|
|
m_notifyTimer.start(msec, true);
|
|
}
|
|
|
|
void Server::notifyTimeout()
|
|
{
|
|
// Notify delay time is over, send ISON request if desired
|
|
if (Preferences::useNotify())
|
|
{
|
|
// But only if there actually are nicks in the notify list
|
|
TQString list = getISONListString();
|
|
|
|
if (!list.isEmpty()) queue("ISON "+list, LowPriority);
|
|
|
|
}
|
|
}
|
|
|
|
void Server::autoCommandsAndChannels()
|
|
{
|
|
if (getServerGroup() && !getServerGroup()->connectCommands().isEmpty())
|
|
{
|
|
TQString connectCommands = getServerGroup()->connectCommands();
|
|
|
|
if (!getNickname().isEmpty())
|
|
connectCommands.replace("%nick", getNickname());
|
|
|
|
TQStringList connectCommandsList = TQStringList::split(";", connectCommands);
|
|
TQStringList::iterator iter;
|
|
|
|
for (iter = connectCommandsList.begin(); iter != connectCommandsList.end(); ++iter)
|
|
{
|
|
TQString output(*iter);
|
|
output = output.simplifyWhiteSpace();
|
|
getOutputFilter()->replaceAliases(output);
|
|
Konversation::OutputFilterResult result = getOutputFilter()->parse(getNickname(),output,TQString());
|
|
queue(result.toServer);
|
|
}
|
|
}
|
|
|
|
if (getAutoJoin())
|
|
{
|
|
for ( TQStringList::Iterator it = m_autoJoinCommands.begin(); it != m_autoJoinCommands.end(); ++it )
|
|
queue((*it));
|
|
}
|
|
}
|
|
|
|
/** Create a set of indices into the nickname list of the current identity based on the current nickname.
|
|
*
|
|
* The index list is only used if the current nickname is not available. If the nickname is in the identity,
|
|
* we do not want to retry it. If the nickname is not in the identity, it is considered to be at position -1.
|
|
*/
|
|
void Server::resetNickSelection()
|
|
{
|
|
m_nickIndices.clear();
|
|
//for equivalence testing in case the identity gets changed underneath us
|
|
m_referenceNicklist = getIdentity()->getNicknameList();
|
|
//where in this identities nicklist will we have started?
|
|
int start = m_referenceNicklist.findIndex(getNickname());
|
|
int len = m_referenceNicklist.count();
|
|
|
|
//we first use this list of indices *after* we've already tried the current nick, which we don't want
|
|
//to retry if we wrapped, so exclude its index here
|
|
//if it wasn't in the list, we get -1 back, so then we *want* to include 0
|
|
for (int i=start+1; i<len; i++)
|
|
m_nickIndices.append(i);
|
|
//now, from the beginning of the list, to the item before start
|
|
for (int i=0; i<start; i++)
|
|
m_nickIndices.append(i);
|
|
//cause it to try to get an invalid nick number
|
|
m_nickIndices.append(len);
|
|
}
|
|
|
|
TQString Server::getNextNickname()
|
|
{
|
|
//if the identity changed underneath us (likely impossible), start over
|
|
if (m_referenceNicklist != getIdentity()->getNicknameList())
|
|
resetNickSelection();
|
|
|
|
TQString newNick = getIdentity()->getNickname(m_nickIndices.front());
|
|
m_nickIndices.pop_front();
|
|
|
|
if (newNick.isNull())
|
|
{
|
|
TQString inputText = i18n("No nicknames from the \"%1\" identity were accepted by the connection \"%2\".\nPlease enter a new one or press Cancel to disconnect:").arg(getIdentity()->getName()).arg(getDisplayName());
|
|
newNick = KInputDialog::getText(i18n("Nickname error"), inputText,
|
|
TQString(), 0, getStatusView(), "NickChangeDialog");
|
|
}
|
|
|
|
return newNick;
|
|
}
|
|
|
|
void Server::processIncomingData()
|
|
{
|
|
m_incomingTimer.stop();
|
|
|
|
if (!m_inputBuffer.isEmpty() && !m_processingIncoming)
|
|
{
|
|
m_processingIncoming = true;
|
|
TQString front(m_inputBuffer.front());
|
|
m_inputBuffer.pop_front();
|
|
if (m_rawLog)
|
|
{
|
|
TQString toRaw = front;
|
|
m_rawLog->appendRaw(">> " + toRaw.replace("&","&").replace("<","<").replace(">",">").replace(TQRegExp("\\s"), " "));
|
|
}
|
|
m_inputFilter.parseLine(front);
|
|
m_processingIncoming = false;
|
|
|
|
if (!m_inputBuffer.isEmpty()) m_incomingTimer.start(0);
|
|
}
|
|
}
|
|
|
|
void Server::incoming()
|
|
{
|
|
if (getConnectionSettings().server().SSLEnabled())
|
|
emit sslConnected(this);
|
|
|
|
// We read all available bytes here because readyRead() signal will be emitted when there is new data
|
|
// else we will stall when displaying MOTD etc.
|
|
int max_bytes = m_socket->bytesAvailable();
|
|
|
|
TQByteArray buffer(max_bytes+1);
|
|
int len = 0;
|
|
|
|
// Read at max "max_bytes" bytes into "buffer"
|
|
len = m_socket->readBlock(buffer.data(),max_bytes);
|
|
|
|
if (len <= 0 && getConnectionSettings().server().SSLEnabled())
|
|
return;
|
|
|
|
if (len <= 0) // Zero means buffer is empty which shouldn't happen because readyRead signal is emitted
|
|
{
|
|
getStatusView()->appendServerMessage(i18n("Error"),
|
|
i18n("There was an error reading the data from the server: %1").
|
|
arg(m_socket->TDESocketBase::errorString()));
|
|
|
|
broken(m_socket->error());
|
|
return;
|
|
}
|
|
|
|
buffer[len] = 0;
|
|
|
|
TQCString qcsBuffer = m_inputBufferIncomplete + TQCString(buffer);
|
|
|
|
// split buffer to lines
|
|
TQValueList<TQCString> qcsBufferLines;
|
|
int lastLFposition = -1;
|
|
for( int nextLFposition ; ( nextLFposition = qcsBuffer.find('\n', lastLFposition+1) ) != -1 ; lastLFposition = nextLFposition )
|
|
qcsBufferLines << qcsBuffer.mid(lastLFposition+1, nextLFposition-lastLFposition-1);
|
|
|
|
// remember the incomplete line (split by packets)
|
|
m_inputBufferIncomplete = qcsBuffer.right(qcsBuffer.length()-lastLFposition-1);
|
|
|
|
while(!qcsBufferLines.isEmpty())
|
|
{
|
|
// Pre parsing is needed in case encryption/decryption is needed
|
|
// BEGIN set channel encoding if specified
|
|
TQString senderNick;
|
|
bool isServerMessage = false;
|
|
TQString channelKey;
|
|
TQTextCodec* codec = getIdentity()->getCodec();
|
|
TQCString front = qcsBufferLines.front();
|
|
|
|
TQStringList lineSplit = TQStringList::split(" ",codec->toUnicode(front));
|
|
|
|
if( lineSplit.count() >= 1 )
|
|
{
|
|
if( lineSplit[0][0] == ':' ) // does this message have a prefix?
|
|
{
|
|
if( !lineSplit[0].contains('!') ) // is this a server(global) message?
|
|
isServerMessage = true;
|
|
else
|
|
senderNick = lineSplit[0].mid(1, lineSplit[0].find('!')-1);
|
|
|
|
lineSplit.pop_front(); // remove prefix
|
|
}
|
|
}
|
|
|
|
// BEGIN pre-parse to know where the message belongs to
|
|
TQString command = lineSplit[0].lower();
|
|
if( isServerMessage )
|
|
{
|
|
if( lineSplit.count() >= 3 )
|
|
{
|
|
if( command == "332" ) // RPL_TOPIC
|
|
channelKey = lineSplit[2];
|
|
if( command == "372" ) // RPL_MOTD
|
|
channelKey = ":server";
|
|
}
|
|
}
|
|
else // NOT a global message
|
|
{
|
|
if( lineSplit.count() >= 2 )
|
|
{
|
|
// query
|
|
if( ( command == "privmsg" ||
|
|
command == "notice" ) &&
|
|
lineSplit[1] == getNickname() )
|
|
{
|
|
channelKey = senderNick;
|
|
}
|
|
// channel message
|
|
else if( command == "privmsg" ||
|
|
command == "notice" ||
|
|
command == "join" ||
|
|
command == "kick" ||
|
|
command == "part" ||
|
|
command == "topic" )
|
|
{
|
|
channelKey = lineSplit[1];
|
|
}
|
|
}
|
|
}
|
|
// END pre-parse to know where the message belongs to
|
|
// Decrypt if necessary
|
|
if(command == "privmsg")
|
|
Konversation::decrypt(channelKey,front,this);
|
|
else if(command == "332" || command == "topic")
|
|
{
|
|
Konversation::decryptTopic(channelKey,front,this);
|
|
}
|
|
|
|
bool isUtf8 = Konversation::isUtf8(front);
|
|
|
|
if( isUtf8 )
|
|
m_inputBuffer << TQString::fromUtf8(front);
|
|
else
|
|
{
|
|
// check setting
|
|
TQString channelEncoding;
|
|
if( !channelKey.isEmpty() )
|
|
{
|
|
channelEncoding = Preferences::channelEncoding(getDisplayName(), channelKey);
|
|
}
|
|
// END set channel encoding if specified
|
|
|
|
if( !channelEncoding.isEmpty() )
|
|
codec = Konversation::IRCCharsets::self()->codecForName(channelEncoding);
|
|
|
|
// if channel encoding is utf-8 and the string is definitely not utf-8
|
|
// then try latin-1
|
|
if ( !isUtf8 && codec->mibEnum() == 106 )
|
|
codec = TQTextCodec::codecForMib( 4 /* iso-8859-1 */ );
|
|
|
|
m_inputBuffer << codec->toUnicode(front);
|
|
}
|
|
qcsBufferLines.pop_front();
|
|
m_bytesReceived+=m_inputBuffer.back().length();
|
|
}
|
|
|
|
if( !m_incomingTimer.isActive() && !m_processingIncoming )
|
|
m_incomingTimer.start(0);
|
|
}
|
|
|
|
/** Calculate how long this message premable will be.
|
|
|
|
This is necessary because the irc server will clip messages so that the
|
|
client receives a maximum of 512 bytes at once.
|
|
*/
|
|
int Server::getPreLength(const TQString& command, const TQString& dest)
|
|
{
|
|
NickInfo* info = getNickInfo(getNickname());
|
|
int hostMaskLength = 0;
|
|
|
|
if(info)
|
|
hostMaskLength = info->getHostmask().length();
|
|
|
|
//:Sho_!i=ehs1@konversation/developer/hein PRIVMSG #konversation :and then back to it
|
|
|
|
//<colon>$nickname<!>$hostmask<space>$command<space>$destination<space><colon>$message<cr><lf>
|
|
int x= 512 - 8 - (m_nickname.length() + hostMaskLength + command.length() + dest.length());
|
|
|
|
return x;
|
|
}
|
|
|
|
//Commands greater than 1 have localizeable text: 0 1 2 3 4 5 6
|
|
static TQStringList outcmds=TQStringList::split(TQChar(' '),"WHO QUIT PRIVMSG NOTICE KICK PART TOPIC");
|
|
|
|
int Server::_send_internal(TQString outputLine)
|
|
{
|
|
TQStringList outputLineSplit=TQStringList::split(" ", outputLine);
|
|
//Lets cache the uppercase command so we don't miss or reiterate too much
|
|
int outboundCommand=outcmds.findIndex(outputLineSplit[0].upper());
|
|
|
|
if (outputLine.at(outputLine.length()-1) == '\n')
|
|
{
|
|
kdDebug() << "found \\n on " << outboundCommand << endl;
|
|
outputLine.setLength(outputLine.length()-1);
|
|
}
|
|
|
|
// remember the first arg of /WHO to identify responses
|
|
if (outboundCommand == 0) //"WHO"
|
|
{
|
|
if (outputLineSplit.count() >= 2)
|
|
m_inputFilter.addWhoRequest(outputLineSplit[1]);
|
|
else // no argument (servers recognize it as "*")
|
|
m_inputFilter.addWhoRequest("*");
|
|
}
|
|
else if (outboundCommand == 1) //"QUIT"
|
|
updateConnectionState(Konversation::SSDeliberatelyDisconnected);
|
|
|
|
// set channel encoding if specified
|
|
TQString channelCodecName;
|
|
|
|
//[ PRIVMSG | NOTICE | KICK | PART | TOPIC ] target :message
|
|
if (outputLineSplit.count() > 2 && outboundCommand > 1)
|
|
channelCodecName=Preferences::channelEncoding(getDisplayName(), outputLineSplit[1]);
|
|
|
|
TQTextCodec* codec;
|
|
if (channelCodecName.isEmpty())
|
|
codec = getIdentity()->getCodec();
|
|
else
|
|
codec = Konversation::IRCCharsets::self()->codecForName(channelCodecName);
|
|
|
|
// Some codecs don't work with a negative value. This is a bug in TQt 3.
|
|
// ex.: JIS7, eucJP, SJIS
|
|
//int outlen=-1;
|
|
int outlen=outputLine.length();
|
|
|
|
//leaving this done twice for now, i'm uncertain of the implications of not encoding other commands
|
|
TQCString encoded=codec->fromUnicode(outputLine, outlen);
|
|
|
|
TQString blowfishKey=getKeyForRecipient(outputLineSplit[1]);
|
|
if (!blowfishKey.isEmpty() && outboundCommand >1)
|
|
{
|
|
int colon = outputLine.find(':');
|
|
if (colon > -1)
|
|
{
|
|
colon++;
|
|
|
|
TQString pay(outputLine.mid(colon));
|
|
int len=pay.length();
|
|
//only encode the actual user text, IRCD *should* desire only ASCII 31 < x < 127 for protocol elements
|
|
TQCString payload=codec->fromUnicode(pay, len);
|
|
//apparently channel name isn't a protocol element...
|
|
len=outputLineSplit[1].length();
|
|
TQCString dest=codec->fromUnicode(outputLineSplit[1], len);
|
|
|
|
if (outboundCommand == 2 || outboundCommand == 6) // outboundCommand == 3
|
|
{
|
|
bool doit = true;
|
|
if (outboundCommand == 2)
|
|
{
|
|
//if its a privmsg and a ctcp but not an action, don't encrypt
|
|
//not interpreting `payload` in case encoding bollixed it
|
|
if (outputLineSplit[2].startsWith(":\x01") && outputLineSplit[2] != ":\x01""ACTION")
|
|
doit = false;
|
|
}
|
|
if (doit)
|
|
{
|
|
Konversation::encrypt(blowfishKey, payload);
|
|
encoded = outputLineSplit[0].ascii();
|
|
//two lines because the compiler insists on using the wrong operator+
|
|
encoded += ' ' + dest + " :" + payload;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
encoded += '\n';
|
|
TQ_LONG sout = m_socket->writeBlock(encoded, encoded.length());
|
|
|
|
if (m_rawLog)
|
|
m_rawLog->appendRaw("<< " + outputLine.replace("&","&").replace("<","<").replace(">",">"));
|
|
|
|
return sout;
|
|
}
|
|
|
|
void Server::toServer(TQString&s, IRCQueue* q)
|
|
{
|
|
|
|
int sizesent = _send_internal(s);
|
|
emit sentStat(s.length(), sizesent, q); //tell the queues what we sent
|
|
//tell everyone else
|
|
emit sentStat(s.length(), sizesent);
|
|
}
|
|
|
|
void Server::collectStats(int bytes, int encodedBytes)
|
|
{
|
|
m_bytesSent += bytes;
|
|
m_encodedBytesSent += encodedBytes;
|
|
m_linesSent++;
|
|
}
|
|
|
|
bool Server::validQueue(QueuePriority priority)
|
|
{
|
|
if (priority >=0 && priority <= _max_queue())
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
bool Server::queue(const TQString& line, QueuePriority priority)
|
|
{
|
|
if (!line.isEmpty() && validQueue(priority))
|
|
{
|
|
IRCQueue& out=*m_queues[priority];
|
|
out.enqueue(line);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool Server::queueList(const TQStringList& buffer, QueuePriority priority)
|
|
{
|
|
if (buffer.isEmpty() || !validQueue(priority))
|
|
return false;
|
|
|
|
IRCQueue& out=*(m_queues[priority]);
|
|
|
|
for(unsigned int i=0;i<buffer.count();i++)
|
|
{
|
|
TQString line=*buffer.at(i);
|
|
if (!line.isEmpty())
|
|
out.enqueue(line);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void Server::resetQueues()
|
|
{
|
|
for (int i=0;i<=_max_queue();i++)
|
|
m_queues[i]->reset();
|
|
}
|
|
|
|
//this could flood you off, but you're leaving anyway...
|
|
void Server::flushQueues()
|
|
{
|
|
int cue;
|
|
do
|
|
{
|
|
cue=-1;
|
|
int wait=0;
|
|
for (int i=1;i<=_max_queue();i++) //slow queue can rot
|
|
{
|
|
IRCQueue *queue=m_queues[i];
|
|
//higher queue indices have higher priorty, higher queue priority wins tie
|
|
if (!queue->isEmpty() && queue->currentWait()>=wait)
|
|
{
|
|
cue=i;
|
|
wait=queue->currentWait();
|
|
}
|
|
}
|
|
if (cue>-1)
|
|
m_queues[cue]->sendNow();
|
|
} while (cue>-1);
|
|
}
|
|
|
|
void Server::closed()
|
|
{
|
|
broken(m_socket->error());
|
|
}
|
|
|
|
void Server::dcopRaw(const TQString& command)
|
|
{
|
|
if(command.startsWith(Preferences::commandChar()))
|
|
{
|
|
queue(command.section(Preferences::commandChar(), 1));
|
|
}
|
|
else
|
|
queue(command);
|
|
}
|
|
|
|
void Server::dcopSay(const TQString& target,const TQString& command)
|
|
{
|
|
if(isAChannel(target))
|
|
{
|
|
Channel* channel=getChannelByName(target);
|
|
if(channel) channel->sendChannelText(command);
|
|
}
|
|
else
|
|
{
|
|
class Query* query=getQueryByName(target);
|
|
if(query==0)
|
|
{
|
|
NickInfoPtr nickinfo = obtainNickInfo(target);
|
|
query=addQuery(nickinfo, true);
|
|
}
|
|
if(query)
|
|
{
|
|
if(!command.isEmpty())
|
|
query->sendQueryText(command);
|
|
else
|
|
{
|
|
query->adjustFocus();
|
|
getViewContainer()->getWindow()->show();
|
|
KWin::demandAttention(getViewContainer()->getWindow()->winId());
|
|
KWin::activateWindow(getViewContainer()->getWindow()->winId());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void Server::dcopInfo(const TQString& string)
|
|
{
|
|
appendMessageToFrontmost(i18n("DCOP"),string);
|
|
}
|
|
|
|
void Server::ctcpReply(const TQString &receiver,const TQString &text)
|
|
{
|
|
queue("NOTICE "+receiver+" :"+'\x01'+text+'\x01');
|
|
}
|
|
|
|
// Given a nickname, returns NickInfo object. 0 if not found.
|
|
NickInfoPtr Server::getNickInfo(const TQString& nickname)
|
|
{
|
|
TQString lcNickname(nickname.lower());
|
|
if (m_allNicks.contains(lcNickname))
|
|
{
|
|
NickInfoPtr nickinfo = m_allNicks[lcNickname];
|
|
Q_ASSERT(nickinfo);
|
|
return nickinfo;
|
|
}
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
// Given a nickname, returns an existing NickInfo object, or creates a new NickInfo object.
|
|
// Returns pointer to the found or created NickInfo object.
|
|
NickInfoPtr Server::obtainNickInfo(const TQString& nickname)
|
|
{
|
|
NickInfoPtr nickInfo = getNickInfo(nickname);
|
|
if (!nickInfo)
|
|
{
|
|
nickInfo = new NickInfo(nickname, this);
|
|
m_allNicks.insert(TQString(nickname.lower()), nickInfo);
|
|
}
|
|
return nickInfo;
|
|
}
|
|
|
|
const NickInfoMap* Server::getAllNicks() { return &m_allNicks; }
|
|
|
|
// Returns the list of members for a channel in the joinedChannels list.
|
|
// 0 if channel is not in the joinedChannels list.
|
|
// Using code must not alter the list.
|
|
const ChannelNickMap *Server::getJoinedChannelMembers(const TQString& channelName) const
|
|
{
|
|
TQString lcChannelName = channelName.lower();
|
|
if (m_joinedChannels.contains(lcChannelName))
|
|
return m_joinedChannels[lcChannelName];
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
// Returns the list of members for a channel in the unjoinedChannels list.
|
|
// 0 if channel is not in the unjoinedChannels list.
|
|
// Using code must not alter the list.
|
|
const ChannelNickMap *Server::getUnjoinedChannelMembers(const TQString& channelName) const
|
|
{
|
|
TQString lcChannelName = channelName.lower();
|
|
if (m_unjoinedChannels.contains(lcChannelName))
|
|
return m_unjoinedChannels[lcChannelName];
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
// Searches the Joined and Unjoined lists for the given channel and returns the member list.
|
|
// 0 if channel is not in either list.
|
|
// Using code must not alter the list.
|
|
const ChannelNickMap *Server::getChannelMembers(const TQString& channelName) const
|
|
{
|
|
const ChannelNickMap *members = getJoinedChannelMembers(channelName);
|
|
if (members)
|
|
return members;
|
|
else
|
|
return getUnjoinedChannelMembers(channelName);
|
|
}
|
|
|
|
// Returns pointer to the ChannelNick (mode and pointer to NickInfo) for a given channel and nickname.
|
|
// 0 if not found.
|
|
ChannelNickPtr Server::getChannelNick(const TQString& channelName, const TQString& nickname)
|
|
{
|
|
TQString lcNickname = nickname.lower();
|
|
const ChannelNickMap *channelNickMap = getChannelMembers(channelName);
|
|
if (channelNickMap)
|
|
{
|
|
if (channelNickMap->contains(lcNickname))
|
|
return (*channelNickMap)[lcNickname];
|
|
else
|
|
return 0;
|
|
}
|
|
else
|
|
{
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
// Updates a nickname in a channel. If not on the joined or unjoined lists, and nick
|
|
// is in the watch list, adds the channel and nick to the unjoinedChannels list.
|
|
// If mode != 99, sets the mode for the nick in the channel.
|
|
// Returns the NickInfo object if nick is on any lists, otherwise 0.
|
|
ChannelNickPtr Server::setChannelNick(const TQString& channelName, const TQString& nickname, unsigned int mode)
|
|
{
|
|
TQString lcNickname = nickname.lower();
|
|
// If already on a list, update mode.
|
|
ChannelNickPtr channelNick = getChannelNick(channelName, lcNickname);
|
|
if (!channelNick)
|
|
{
|
|
// Get watch list from preferences.
|
|
TQString watchlist=getWatchListString();
|
|
// Create a lower case nick list from the watch list.
|
|
TQStringList watchLowerList=TQStringList::split(' ',watchlist.lower());
|
|
// If on the watch list, add channel and nick to unjoinedChannels list.
|
|
if (watchLowerList.find(lcNickname) != watchLowerList.end())
|
|
{
|
|
channelNick = addNickToUnjoinedChannelsList(channelName, nickname);
|
|
channelNick->setMode(mode);
|
|
}
|
|
else return 0;
|
|
}
|
|
|
|
if (mode != 99) channelNick->setMode(mode);
|
|
return channelNick;
|
|
}
|
|
|
|
// Returns a list of all the joined channels that a nick is in.
|
|
TQStringList Server::getNickJoinedChannels(const TQString& nickname)
|
|
{
|
|
TQString lcNickname = nickname.lower();
|
|
TQStringList channellist;
|
|
ChannelMembershipMap::ConstIterator channel;
|
|
for( channel = m_joinedChannels.begin(); channel != m_joinedChannels.end(); ++channel )
|
|
{
|
|
if (channel.data()->contains(lcNickname)) channellist.append(channel.key());
|
|
}
|
|
return channellist;
|
|
}
|
|
|
|
// Returns a list of all the channels (joined or unjoined) that a nick is in.
|
|
TQStringList Server::getNickChannels(const TQString& nickname)
|
|
{
|
|
TQString lcNickname = nickname.lower();
|
|
TQStringList channellist;
|
|
ChannelMembershipMap::ConstIterator channel;
|
|
for( channel = m_joinedChannels.begin(); channel != m_joinedChannels.end(); ++channel )
|
|
{
|
|
if (channel.data()->contains(lcNickname)) channellist.append(channel.key());
|
|
}
|
|
for( channel = m_unjoinedChannels.begin(); channel != m_unjoinedChannels.end(); ++channel )
|
|
{
|
|
if (channel.data()->contains(lcNickname)) channellist.append(channel.key());
|
|
}
|
|
return channellist;
|
|
}
|
|
|
|
bool Server::isNickOnline(const TQString &nickname)
|
|
{
|
|
NickInfoPtr nickInfo = getNickInfo(nickname);
|
|
return (nickInfo != 0);
|
|
}
|
|
|
|
TQString Server::getOwnIpByNetworkInterface()
|
|
{
|
|
return m_socket->localAddress().nodeName();
|
|
}
|
|
|
|
TQString Server::getOwnIpByServerMessage()
|
|
{
|
|
if(!m_ownIpByWelcome.isEmpty())
|
|
return m_ownIpByWelcome;
|
|
else if(!m_ownIpByUserhost.isEmpty())
|
|
return m_ownIpByUserhost;
|
|
else
|
|
return TQString();
|
|
}
|
|
|
|
class Query* Server::addQuery(const NickInfoPtr & nickInfo, bool weinitiated)
|
|
{
|
|
TQString nickname = nickInfo->getNickname();
|
|
// Only create new query object if there isn't already one with the same name
|
|
class Query* query=getQueryByName(nickname);
|
|
|
|
if (!query)
|
|
{
|
|
TQString lcNickname = nickname.lower();
|
|
query = getViewContainer()->addQuery(this, nickInfo, weinitiated);
|
|
|
|
connect(query, TQT_SIGNAL(sendFile(const TQString&)),this, TQT_SLOT(requestDccSend(const TQString&)));
|
|
connect(this, TQT_SIGNAL(serverOnline(bool)), query, TQT_SLOT(serverOnline(bool)));
|
|
|
|
// Append query to internal list
|
|
m_queryList.append(query);
|
|
|
|
m_queryNicks.insert(lcNickname, nickInfo);
|
|
|
|
if (!weinitiated)
|
|
static_cast<KonversationApplication*>(kapp)->notificationHandler()->query(query, nickname);
|
|
}
|
|
|
|
// try to get hostmask if there's none yet
|
|
if (query->getNickInfo()->getHostmask().isEmpty()) requestUserhost(nickname);
|
|
|
|
Q_ASSERT(query);
|
|
|
|
return query;
|
|
}
|
|
|
|
void Server::closeQuery(const TQString &name)
|
|
{
|
|
class Query* query = getQueryByName(name);
|
|
removeQuery(query);
|
|
|
|
// Update NickInfo. If no longer on any lists, delete it altogether, but
|
|
// only if not on the watch list. ISON replies will determine whether the NickInfo
|
|
// is deleted altogether in that case.
|
|
TQString lcNickname = name.lower();
|
|
m_queryNicks.remove(lcNickname);
|
|
if (!isWatchedNick(name)) deleteNickIfUnlisted(name);
|
|
}
|
|
|
|
void Server::closeChannel(const TQString& name)
|
|
{
|
|
kdDebug() << "Server::closeChannel(" << name << ")" << endl;
|
|
Channel* channelToClose = getChannelByName(name);
|
|
|
|
if(channelToClose)
|
|
{
|
|
Konversation::OutputFilterResult result = getOutputFilter()->parse(getNickname(),
|
|
Preferences::commandChar() + "PART", name);
|
|
queue(result.toServer);
|
|
}
|
|
}
|
|
|
|
void Server::requestChannelList()
|
|
{
|
|
m_inputFilter.setAutomaticRequest("LIST", TQString(), true);
|
|
queue(TQString("LIST"));
|
|
}
|
|
|
|
void Server::requestWhois(const TQString& nickname)
|
|
{
|
|
m_inputFilter.setAutomaticRequest("WHOIS", nickname, true);
|
|
queue("WHOIS "+nickname, LowPriority);
|
|
}
|
|
|
|
void Server::requestWho(const TQString& channel)
|
|
{
|
|
m_inputFilter.setAutomaticRequest("WHO", channel, true);
|
|
queue("WHO "+channel, LowPriority);
|
|
}
|
|
|
|
void Server::requestUserhost(const TQString& nicks)
|
|
{
|
|
TQStringList nicksList = TQStringList::split(" ", nicks);
|
|
for(TQStringList::ConstIterator it=nicksList.begin() ; it!=nicksList.end() ; ++it)
|
|
m_inputFilter.setAutomaticRequest("USERHOST", *it, true);
|
|
queue("USERHOST "+nicks, LowPriority);
|
|
}
|
|
|
|
void Server::requestTopic(const TQString& channel)
|
|
{
|
|
m_inputFilter.setAutomaticRequest("TOPIC", channel, true);
|
|
queue("TOPIC "+channel, LowPriority);
|
|
}
|
|
|
|
void Server::resolveUserhost(const TQString& nickname)
|
|
{
|
|
m_inputFilter.setAutomaticRequest("WHOIS", nickname, true);
|
|
m_inputFilter.setAutomaticRequest("DNS", nickname, true);
|
|
queue("WHOIS "+nickname, LowPriority); //FIXME when is this really used?
|
|
}
|
|
|
|
void Server::requestBan(const TQStringList& users,const TQString& channel,const TQString& a_option)
|
|
{
|
|
TQString hostmask;
|
|
TQString option=a_option.lower();
|
|
|
|
Channel* targetChannel=getChannelByName(channel);
|
|
|
|
for(unsigned int index=0;index<users.count();index++)
|
|
{
|
|
// first, set the ban mask to the specified nick
|
|
TQString mask=users[index];
|
|
// did we specify an option?
|
|
if(!option.isEmpty())
|
|
{
|
|
// try to find specified nick on the channel
|
|
Nick* targetNick=targetChannel->getNickByName(mask);
|
|
// if we found the nick try to find their hostmask
|
|
if(targetNick)
|
|
{
|
|
TQString hostmask=targetNick->getChannelNick()->getHostmask();
|
|
// if we found the hostmask, add it to the ban mask
|
|
if(!hostmask.isEmpty())
|
|
{
|
|
mask=targetNick->getChannelNick()->getNickname()+'!'+hostmask;
|
|
|
|
// adapt ban mask to the option given
|
|
if(option=="host")
|
|
mask="*!*@*."+hostmask.section('.',1);
|
|
else if(option=="domain")
|
|
mask="*!*@"+hostmask.section('@',1);
|
|
else if(option=="userhost")
|
|
mask="*!"+hostmask.section('@',0,0)+"@*."+hostmask.section('.',1);
|
|
else if(option=="userdomain")
|
|
mask="*!"+hostmask.section('@',0,0)+'@'+hostmask.section('@',1);
|
|
}
|
|
}
|
|
}
|
|
|
|
Konversation::OutputFilterResult result = getOutputFilter()->execBan(mask,channel);
|
|
queue(result.toServer);
|
|
}
|
|
}
|
|
|
|
void Server::requestUnban(const TQString& mask,const TQString& channel)
|
|
{
|
|
Konversation::OutputFilterResult result = getOutputFilter()->execUnban(mask,channel);
|
|
queue(result.toServer);
|
|
}
|
|
|
|
void Server::requestDccSend()
|
|
{
|
|
requestDccSend(TQString());
|
|
}
|
|
|
|
void Server::sendURIs(const TQStrList& uris, const TQString& nick)
|
|
{
|
|
for (TQStrListIterator it(uris) ; *it; ++it)
|
|
addDccSend(nick,KURL(*it));
|
|
}
|
|
|
|
void Server::requestDccSend(const TQString &a_recipient)
|
|
{
|
|
TQString recipient(a_recipient);
|
|
// if we don't have a recipient yet, let the user select one
|
|
if(recipient.isEmpty())
|
|
{
|
|
TQStringList nickList;
|
|
Channel* lookChannel=m_channelList.first();
|
|
|
|
// fill nickList with all nicks we know about
|
|
while (lookChannel)
|
|
{
|
|
TQPtrList<Nick> nicks=lookChannel->getNickList();
|
|
Nick* lookNick=nicks.first();
|
|
while(lookNick)
|
|
{
|
|
if(!nickList.contains(lookNick->getChannelNick()->getNickname())) nickList.append(lookNick->getChannelNick()->getNickname());
|
|
lookNick=nicks.next();
|
|
}
|
|
lookChannel=m_channelList.next();
|
|
}
|
|
|
|
// add Queries as well, but don't insert duplicates
|
|
class Query* lookQuery=m_queryList.first();
|
|
while(lookQuery)
|
|
{
|
|
if(!nickList.contains(lookQuery->getName())) nickList.append(lookQuery->getName());
|
|
lookQuery=m_queryList.next();
|
|
}
|
|
|
|
recipient=DccRecipientDialog::getNickname(getViewContainer()->getWindow(),nickList);
|
|
}
|
|
// do we have a recipient *now*?
|
|
if(!recipient.isEmpty())
|
|
{
|
|
KURL::List fileURLs=KFileDialog::getOpenURLs(
|
|
":lastDccDir",
|
|
TQString(),
|
|
getViewContainer()->getWindow(),
|
|
i18n("Select File(s) to Send to %1").arg(recipient)
|
|
);
|
|
KURL::List::iterator it;
|
|
for ( it = fileURLs.begin() ; it != fileURLs.end() ; ++it )
|
|
{
|
|
addDccSend( recipient, *it );
|
|
}
|
|
}
|
|
}
|
|
|
|
void Server::slotNewDccTransferItemQueued(DccTransfer* transfer)
|
|
{
|
|
if (transfer->getConnectionId() == connectionId() )
|
|
{
|
|
kdDebug() << "Server::slotNewDccTranfserItemQueued(): connecting slots for " << transfer->getFileName() << " [" << transfer->getType() << "]" << endl;
|
|
if ( transfer->getType() == DccTransfer::Receive )
|
|
{
|
|
connect( transfer, TQT_SIGNAL( done( DccTransfer* ) ), this, TQT_SLOT( dccGetDone( DccTransfer* ) ) );
|
|
connect( transfer, TQT_SIGNAL( statusChanged( DccTransfer*, int, int ) ), this, TQT_SLOT( dccStatusChanged( DccTransfer*, int, int ) ) );
|
|
}
|
|
else
|
|
{
|
|
connect( transfer, TQT_SIGNAL( done( DccTransfer* ) ), this, TQT_SLOT( dccSendDone( DccTransfer* ) ) );
|
|
connect( transfer, TQT_SIGNAL( statusChanged( DccTransfer*, int, int ) ), this, TQT_SLOT( dccStatusChanged( DccTransfer*, int, int ) ) );
|
|
}
|
|
}
|
|
}
|
|
|
|
void Server::addDccSend(const TQString &recipient,KURL fileURL, const TQString &altFileName, uint fileSize)
|
|
{
|
|
if (!fileURL.isValid()) return;
|
|
|
|
emit addDccPanel();
|
|
|
|
// We already checked that the file exists in output filter / requestDccSend() resp.
|
|
DccTransferSend* newDcc = KonversationApplication::instance()->getDccTransferManager()->newUpload();
|
|
|
|
newDcc->setConnectionId( connectionId() );
|
|
|
|
newDcc->setPartnerNick( recipient );
|
|
newDcc->setFileURL( fileURL );
|
|
if ( !altFileName.isEmpty() )
|
|
newDcc->setFileName( altFileName );
|
|
if ( fileSize != 0 )
|
|
newDcc->setFileSize( fileSize );
|
|
|
|
if ( newDcc->queue() )
|
|
newDcc->start();
|
|
}
|
|
|
|
void Server::addDccGet(const TQString &sourceNick, const TQStringList &dccArguments)
|
|
{
|
|
emit addDccPanel();
|
|
|
|
DccTransferRecv* newDcc = KonversationApplication::instance()->getDccTransferManager()->newDownload();
|
|
|
|
newDcc->setConnectionId( connectionId() );
|
|
|
|
newDcc->setPartnerNick( sourceNick );
|
|
newDcc->setPartnerIp( DccCommon::numericalIpToTextIp( dccArguments[1] ) );
|
|
newDcc->setPartnerPort( dccArguments[2] );
|
|
if ( dccArguments[2] == "0" && dccArguments.count() == 5) // Reverse DCC
|
|
newDcc->setReverse( true, dccArguments[4] );
|
|
|
|
newDcc->setFileName( dccArguments[0] );
|
|
newDcc->setFileSize( dccArguments[3].isEmpty() ? 0 : dccArguments[3].toULong() );
|
|
|
|
if ( newDcc->queue() )
|
|
{
|
|
TQString showfile = newDcc->getFileName();
|
|
|
|
if(showfile.startsWith("\"") && showfile.endsWith("\""))
|
|
showfile = showfile.mid(1, showfile.length() - 2);
|
|
|
|
appendMessageToFrontmost( i18n( "DCC" ),
|
|
i18n( "%1 offers to send you \"%2\" (%3)..." )
|
|
.arg( newDcc->getPartnerNick(),
|
|
showfile,
|
|
( newDcc->getFileSize() == 0 ) ? i18n( "unknown size" ) : TDEIO::convertSize( newDcc->getFileSize() ) ) );
|
|
|
|
if(Preferences::dccAutoGet())
|
|
newDcc->start();
|
|
}
|
|
}
|
|
|
|
void Server::openDccChat(const TQString& nickname)
|
|
{
|
|
emit addDccChat(getNickname(),nickname,TQStringList(),true);
|
|
}
|
|
|
|
void Server::requestDccChat(const TQString& partnerNick, const TQString& numericalOwnIp, const TQString& ownPort)
|
|
{
|
|
queue(TQString("PRIVMSG %1 :\001DCC CHAT chat %2 %3\001").arg(partnerNick).arg(numericalOwnIp).arg(ownPort));
|
|
}
|
|
|
|
void Server::dccSendRequest(const TQString &partner, const TQString &fileName, const TQString &address, const TQString &port, unsigned long size)
|
|
{
|
|
Konversation::OutputFilterResult result = getOutputFilter()->sendRequest(partner,fileName,address,port,size);
|
|
queue(result.toServer);
|
|
|
|
TQString showfile = fileName;
|
|
|
|
if(showfile.startsWith("\"") && showfile.endsWith("\""))
|
|
showfile = showfile.mid(1, showfile.length() - 2);
|
|
|
|
appendMessageToFrontmost( i18n( "DCC" ),
|
|
i18n( "Asking %1 to accept upload of \"%2\" (%3)..." )
|
|
.arg( partner,
|
|
showfile,
|
|
( size == 0 ) ? i18n( "unknown size" ) : TDEIO::convertSize( size ) ) );
|
|
}
|
|
|
|
void Server::dccPassiveSendRequest(const TQString& recipient,const TQString& fileName,const TQString& address,unsigned long size,const TQString& token)
|
|
{
|
|
Konversation::OutputFilterResult result = getOutputFilter()->passiveSendRequest(recipient,fileName,address,size,token);
|
|
queue(result.toServer);
|
|
}
|
|
|
|
void Server::dccResumeGetRequest(const TQString &sender, const TQString &fileName, const TQString &port, TDEIO::filesize_t startAt)
|
|
{
|
|
Konversation::OutputFilterResult result;
|
|
|
|
if (fileName.contains(" ") > 0)
|
|
result = getOutputFilter()->resumeRequest(sender,"\""+fileName+"\"",port,startAt);
|
|
else
|
|
result = getOutputFilter()->resumeRequest(sender,fileName,port,startAt);
|
|
|
|
queue(result.toServer);
|
|
}
|
|
|
|
void Server::dccReverseSendAck(const TQString& partnerNick,const TQString& fileName,const TQString& ownAddress,const TQString& ownPort,unsigned long size,const TQString& reverseToken)
|
|
{
|
|
Konversation::OutputFilterResult result = getOutputFilter()->acceptPassiveSendRequest(partnerNick,fileName,ownAddress,ownPort,size,reverseToken);
|
|
queue(result.toServer);
|
|
}
|
|
|
|
void Server::startReverseDccSendTransfer(const TQString& sourceNick,const TQStringList& dccArguments)
|
|
{
|
|
DccTransferManager* dtm = KonversationApplication::instance()->getDccTransferManager();
|
|
|
|
if ( dtm->startReverseSending( connectionId(), sourceNick,
|
|
dccArguments[0], // filename
|
|
DccCommon::numericalIpToTextIp( dccArguments[1] ), // partner IP
|
|
dccArguments[2], // partner port
|
|
dccArguments[3].toInt(), // filesize
|
|
dccArguments[4] // Reverse DCC token
|
|
) == 0 )
|
|
{
|
|
TQString showfile = dccArguments[0];
|
|
|
|
if(showfile.startsWith("\"") && showfile.endsWith("\""))
|
|
showfile = showfile.mid(1, showfile.length() - 2);
|
|
|
|
// DTM could not find a matched item
|
|
appendMessageToFrontmost( i18n( "Error" ),
|
|
i18n( "%1 = file name, %2 = nickname",
|
|
"Received invalid passive DCC send acceptance message for \"%1\" from %2." )
|
|
.arg( showfile,
|
|
sourceNick ) );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
void Server::resumeDccGetTransfer(const TQString &sourceNick, const TQStringList &dccArguments)
|
|
{
|
|
DccTransferManager* dtm = KonversationApplication::instance()->getDccTransferManager();
|
|
|
|
TQString fileName( dccArguments[0] );
|
|
TQString ownPort( dccArguments[1] );
|
|
unsigned long position = dccArguments[2].toULong();
|
|
|
|
DccTransferRecv* dccTransfer = dtm->resumeDownload( connectionId(), sourceNick, fileName, ownPort, position );
|
|
|
|
TQString showfile = fileName;
|
|
|
|
if(showfile.startsWith("\"") && showfile.endsWith("\""))
|
|
showfile = showfile.mid(1, showfile.length() - 2);
|
|
|
|
if ( dccTransfer )
|
|
{
|
|
appendMessageToFrontmost( i18n( "DCC" ),
|
|
i18n( "%1 = file name, %2 = nickname of sender, %3 = percentage of file size, %4 = file size",
|
|
"Resuming download of \"%1\" from %2 starting at %3% of %4..." )
|
|
.arg( showfile,
|
|
sourceNick,
|
|
TQString::number( dccTransfer->getProgress() ),
|
|
( dccTransfer->getFileSize() == 0 ) ? i18n( "unknown size" ) : TDEIO::convertSize( dccTransfer->getFileSize() ) ) );
|
|
}
|
|
else
|
|
{
|
|
appendMessageToFrontmost( i18n( "Error" ),
|
|
i18n( "%1 = file name, %2 = nickname",
|
|
"Received invalid resume acceptance message for \"%1\" from %2." )
|
|
.arg( showfile,
|
|
sourceNick ) );
|
|
}
|
|
}
|
|
|
|
void Server::resumeDccSendTransfer(const TQString &sourceNick, const TQStringList &dccArguments)
|
|
{
|
|
DccTransferManager* dtm = KonversationApplication::instance()->getDccTransferManager();
|
|
|
|
TQString fileName( dccArguments[0] );
|
|
TQString ownPort( dccArguments[1] );
|
|
unsigned long position = dccArguments[2].toULong();
|
|
|
|
DccTransferSend* dccTransfer = dtm->resumeUpload( connectionId(), sourceNick, fileName, ownPort, position );
|
|
|
|
TQString showfile = fileName;
|
|
|
|
if(showfile.startsWith("\"") && showfile.endsWith("\""))
|
|
showfile = showfile.mid(1, showfile.length() - 2);
|
|
|
|
if ( dccTransfer )
|
|
{
|
|
appendMessageToFrontmost( i18n( "DCC" ),
|
|
i18n( "%1 = file name, %2 = nickname of recipient, %3 = percentage of file size, %4 = file size",
|
|
"Resuming upload of \"%1\" to %2 starting at %3% of %4...")
|
|
.arg( showfile,
|
|
sourceNick,
|
|
TQString::number(dccTransfer->getProgress()),
|
|
( dccTransfer->getFileSize() == 0 ) ? i18n( "unknown size" ) : TDEIO::convertSize( dccTransfer->getFileSize() ) ) );
|
|
|
|
// FIXME: this operation should be done by DccTransferManager
|
|
Konversation::OutputFilterResult result = getOutputFilter()->acceptResumeRequest( sourceNick, fileName, ownPort, position );
|
|
queue( result.toServer );
|
|
|
|
}
|
|
else
|
|
{
|
|
appendMessageToFrontmost( i18n( "Error" ),
|
|
i18n( "%1 = file name, %2 = nickname",
|
|
"Received invalid resume request for \"%1\" from %2." )
|
|
.arg( showfile,
|
|
sourceNick ) );
|
|
}
|
|
}
|
|
|
|
void Server::dccGetDone(DccTransfer* item)
|
|
{
|
|
if (!item)
|
|
return;
|
|
|
|
TQString showfile = item->getFileName();
|
|
|
|
if(showfile.startsWith("\"") && showfile.endsWith("\""))
|
|
showfile = showfile.mid(1, showfile.length() - 2);
|
|
|
|
if(item->getStatus()==DccTransfer::Done)
|
|
appendMessageToFrontmost(i18n("DCC"),i18n("%1 = file name, %2 = nickname of sender",
|
|
"Download of \"%1\" from %2 finished.").arg(showfile, item->getPartnerNick()));
|
|
else if(item->getStatus()==DccTransfer::Failed)
|
|
appendMessageToFrontmost(i18n("DCC"),i18n("%1 = file name, %2 = nickname of sender",
|
|
"Download of \"%1\" from %2 failed. Reason: %3.").arg(showfile,
|
|
item->getPartnerNick(), item->getStatusDetail()));
|
|
}
|
|
|
|
void Server::dccSendDone(DccTransfer* item)
|
|
{
|
|
if (!item)
|
|
return;
|
|
|
|
TQString showfile = item->getFileName();
|
|
|
|
if(showfile.startsWith("\"") && showfile.endsWith("\""))
|
|
showfile = showfile.mid(1, showfile.length() - 2);
|
|
|
|
if(item->getStatus()==DccTransfer::Done)
|
|
appendMessageToFrontmost(i18n("DCC"),i18n("%1 = file name, %2 = nickname of recipient",
|
|
"Upload of \"%1\" to %2 finished.").arg(showfile, item->getPartnerNick()));
|
|
else if(item->getStatus()==DccTransfer::Failed)
|
|
appendMessageToFrontmost(i18n("DCC"),i18n("%1 = file name, %2 = nickname of recipient",
|
|
"Upload of \"%1\" to %2 failed. Reason: %3.").arg(showfile, item->getPartnerNick(),
|
|
item->getStatusDetail()));
|
|
}
|
|
|
|
void Server::dccStatusChanged(DccTransfer *item, int newStatus, int oldStatus)
|
|
{
|
|
if(!item)
|
|
return;
|
|
|
|
TQString showfile = item->getFileName();
|
|
|
|
if(showfile.startsWith("\"") && showfile.endsWith("\""))
|
|
showfile = showfile.mid(1, showfile.length() - 2);
|
|
|
|
if ( item->getType() == DccTransfer::Send )
|
|
{
|
|
// when resuming, a message about the receiver's acceptance has been shown already, so suppress this message
|
|
if ( newStatus == DccTransfer::Transferring && oldStatus == DccTransfer::WaitingRemote && !item->isResumed() )
|
|
appendMessageToFrontmost( i18n( "DCC" ), i18n( "%1 = file name, %2 nickname of recipient",
|
|
"Sending \"%1\" to %2...").arg( showfile, item->getPartnerNick() ) );
|
|
}
|
|
else // type == Receive
|
|
{
|
|
if ( newStatus == DccTransfer::Transferring && !item->isResumed() )
|
|
{
|
|
appendMessageToFrontmost( i18n( "DCC" ),
|
|
i18n( "%1 = file name, %2 = file size, %3 = nickname of sender", "Downloading \"%1\" (%2) from %3...")
|
|
.arg( showfile,
|
|
( item->getFileSize() == 0 ) ? i18n( "unknown size" ) : TDEIO::convertSize( item->getFileSize() ),
|
|
item->getPartnerNick() ) );
|
|
}
|
|
}
|
|
}
|
|
|
|
void Server::removeQuery(class Query* query)
|
|
{
|
|
// Traverse through list to find the query
|
|
class Query* lookQuery = m_queryList.first();
|
|
|
|
while (lookQuery)
|
|
{
|
|
// Did we find our query?
|
|
if (lookQuery == query)
|
|
{
|
|
// Remove it from the query list
|
|
m_queryList.remove(lookQuery);
|
|
// break out of the loop
|
|
lookQuery = 0;
|
|
}
|
|
// else select next query
|
|
else lookQuery = m_queryList.next();
|
|
}
|
|
|
|
query->deleteLater();
|
|
}
|
|
|
|
void Server::sendJoinCommand(const TQString& name, const TQString& password)
|
|
{
|
|
Konversation::OutputFilterResult result = getOutputFilter()->parse(getNickname(),
|
|
Preferences::commandChar() + "JOIN " + name + ' ' + password, TQString());
|
|
queue(result.toServer);
|
|
}
|
|
|
|
void Server::joinChannel(const TQString& name, const TQString& hostmask)
|
|
{
|
|
// (re-)join channel, open a new panel if needed
|
|
Channel* channel = getChannelByName(name);
|
|
|
|
if (!channel)
|
|
{
|
|
channel=getViewContainer()->addChannel(this,name);
|
|
Q_ASSERT(channel);
|
|
channel->setIdentity(getIdentity());
|
|
channel->setNickname(getNickname());
|
|
channel->indicateAway(m_away);
|
|
|
|
if (getServerGroup())
|
|
{
|
|
Konversation::ChannelSettings channelSettings = getServerGroup()->channelByNameFromHistory(name);
|
|
channel->setNotificationsEnabled(channelSettings.enableNotifications());
|
|
getServerGroup()->appendChannelHistory(channelSettings);
|
|
}
|
|
|
|
m_channelList.append(channel);
|
|
|
|
connect(channel,TQT_SIGNAL (sendFile()),this,TQT_SLOT (requestDccSend()) );
|
|
connect(this, TQT_SIGNAL(nicknameChanged(const TQString&)), channel, TQT_SLOT(setNickname(const TQString&)));
|
|
}
|
|
// Move channel from unjoined (if present) to joined list and add our own nickname to the joined list.
|
|
ChannelNickPtr channelNick = addNickToJoinedChannelsList(name, getNickname());
|
|
|
|
if ((channelNick->getHostmask() != hostmask ) && !hostmask.isEmpty())
|
|
{
|
|
NickInfoPtr nickInfo = channelNick->getNickInfo();
|
|
nickInfo->setHostmask(hostmask);
|
|
}
|
|
|
|
channel->joinNickname(channelNick);
|
|
}
|
|
|
|
void Server::removeChannel(Channel* channel)
|
|
{
|
|
// Update NickInfo.
|
|
removeJoinedChannel(channel->getName());
|
|
|
|
if (getServerGroup())
|
|
{
|
|
Konversation::ChannelSettings channelSettings = getServerGroup()->channelByNameFromHistory(channel->getName());
|
|
channelSettings.setNotificationsEnabled(channel->notificationsEnabled());
|
|
getServerGroup()->appendChannelHistory(channelSettings);
|
|
}
|
|
|
|
m_channelList.removeRef(channel);
|
|
}
|
|
|
|
void Server::updateChannelMode(const TQString &updater, const TQString &channelName, char mode, bool plus, const TQString ¶meter)
|
|
{
|
|
|
|
Channel* channel=getChannelByName(channelName);
|
|
|
|
if(channel) //Let the channel be verbose to the screen about the change, and update channelNick
|
|
channel->updateMode(updater, mode, plus, parameter);
|
|
// TODO: What is mode character for owner?
|
|
// Answer from JOHNFLUX - I think that admin is the same as owner. Channel.h has owner as "a"
|
|
// "q" is the likely answer.. UnrealIRCd and euIRCd use it.
|
|
// TODO these need to become dynamic
|
|
TQString userModes="vhoqa"; // voice halfop op owner admin
|
|
int modePos = userModes.find(mode);
|
|
if (modePos > 0)
|
|
{
|
|
ChannelNickPtr updateeNick = getChannelNick(channelName, parameter);
|
|
if(!updateeNick)
|
|
{
|
|
/*
|
|
if(parameter.isEmpty())
|
|
{
|
|
kdDebug() << "in updateChannelMode, a nick with no-name has had their mode '" << mode << "' changed to (" <<plus << ") in channel '" << channelName << "' by " << updater << ". How this happened, I have no idea. Please report this message to irc #konversation if you want to be helpful." << endl << "Ignoring the error and continuing." << endl;
|
|
//this will get their attention.
|
|
kdDebug() << kdBacktrace() << endl;
|
|
}
|
|
else
|
|
{
|
|
kdDebug() << "in updateChannelMode, could not find updatee nick " << parameter << " for channel " << channelName << endl;
|
|
kdDebug() << "This could indicate an obscure race condition that is safely being handled (like the mode of someone changed and they quit almost simulatanously, or it could indicate an internal error." << endl;
|
|
}
|
|
*/
|
|
//TODO Do we need to add this nick?
|
|
return;
|
|
}
|
|
|
|
updateeNick->setMode(mode, plus);
|
|
|
|
// Note that channel will be moved to joined list if necessary.
|
|
addNickToJoinedChannelsList(channelName, parameter);
|
|
}
|
|
|
|
// Update channel ban list.
|
|
if (mode == 'b')
|
|
{
|
|
if (plus)
|
|
{
|
|
TQDateTime when;
|
|
addBan(channelName, TQString("%1 %2 %3").arg(parameter).arg(updater).arg(TQDateTime::currentDateTime().toTime_t()));
|
|
} else {
|
|
removeBan(channelName, parameter);
|
|
}
|
|
}
|
|
}
|
|
|
|
void Server::updateChannelModeWidgets(const TQString &channelName, char mode, const TQString ¶meter)
|
|
{
|
|
Channel* channel=getChannelByName(channelName);
|
|
if(channel) channel->updateModeWidgets(mode,true,parameter);
|
|
}
|
|
|
|
void Server::updateChannelQuickButtons()
|
|
{
|
|
Channel* channel=m_channelList.first();
|
|
|
|
while (channel)
|
|
{
|
|
channel->updateQuickButtons(Preferences::quickButtonList());
|
|
channel = m_channelList.next();
|
|
}
|
|
}
|
|
|
|
Channel* Server::getChannelByName(const TQString& name)
|
|
{
|
|
// Convert wanted channel name to lowercase
|
|
TQString wanted=name;
|
|
wanted=wanted.lower();
|
|
|
|
// Traverse through list to find the channel named "name"
|
|
Channel* lookChannel =m_channelList.first();
|
|
while (lookChannel)
|
|
{
|
|
if (lookChannel->getName().lower()==wanted) return lookChannel;
|
|
lookChannel = m_channelList.next();
|
|
}
|
|
// No channel by that name found? Return 0. Happens on first channel join
|
|
return 0;
|
|
}
|
|
|
|
class Query* Server::getQueryByName(const TQString& name)
|
|
{
|
|
// Convert wanted query name to lowercase
|
|
TQString wanted=name;
|
|
wanted=wanted.lower();
|
|
|
|
// Traverse through list to find the query with "name"
|
|
class Query* lookQuery=m_queryList.first();
|
|
while(lookQuery)
|
|
{
|
|
if(lookQuery->getName().lower()==wanted) return lookQuery;
|
|
lookQuery=m_queryList.next();
|
|
}
|
|
// No query by that name found? Must be a new query request. Return 0
|
|
return 0;
|
|
}
|
|
|
|
void Server::resetNickList(const TQString& channelName)
|
|
{
|
|
Channel* outChannel=getChannelByName(channelName);
|
|
if(outChannel) outChannel->resetNickList();
|
|
}
|
|
|
|
void Server::addPendingNickList(const TQString& channelName,const TQStringList& nickList)
|
|
{
|
|
Channel* outChannel=getChannelByName(channelName);
|
|
if(outChannel) outChannel->addPendingNickList(nickList);
|
|
}
|
|
|
|
// Adds a nickname to the joinedChannels list.
|
|
// Creates new NickInfo if necessary.
|
|
// If needed, moves the channel from the unjoined list to the joined list.
|
|
// Returns the NickInfo for the nickname.
|
|
ChannelNickPtr Server::addNickToJoinedChannelsList(const TQString& channelName, const TQString& nickname)
|
|
{
|
|
bool doChannelJoinedSignal = false;
|
|
bool doWatchedNickChangedSignal = false;
|
|
bool doChannelMembersChangedSignal = false;
|
|
TQString lcNickname = nickname.lower();
|
|
// Create NickInfo if not already created.
|
|
NickInfoPtr nickInfo = getNickInfo(nickname);
|
|
if (!nickInfo)
|
|
{
|
|
nickInfo = new NickInfo(nickname, this);
|
|
m_allNicks.insert(lcNickname, nickInfo);
|
|
doWatchedNickChangedSignal = isWatchedNick(nickname);
|
|
}
|
|
// if nickinfo already exists update nickname, in case we created the nickinfo based
|
|
// on e.g. an incorrectly capitalized ISON request
|
|
else
|
|
nickInfo->setNickname(nickname);
|
|
|
|
// Move the channel from unjoined list (if present) to joined list.
|
|
TQString lcChannelName = channelName.lower();
|
|
ChannelNickMap *channel;
|
|
if (m_unjoinedChannels.contains(lcChannelName))
|
|
{
|
|
channel = m_unjoinedChannels[lcChannelName];
|
|
m_unjoinedChannels.remove(lcChannelName);
|
|
m_joinedChannels.insert(lcChannelName, channel);
|
|
doChannelJoinedSignal = true;
|
|
}
|
|
else
|
|
{
|
|
// Create a new list in the joined channels if not already present.
|
|
if (!m_joinedChannels.contains(lcChannelName))
|
|
{
|
|
channel = new ChannelNickMap;
|
|
m_joinedChannels.insert(lcChannelName, channel);
|
|
doChannelJoinedSignal = true;
|
|
}
|
|
else
|
|
channel = m_joinedChannels[lcChannelName];
|
|
}
|
|
// Add NickInfo to channel list if not already in the list.
|
|
ChannelNickPtr channelNick;
|
|
if (!channel->contains(lcNickname))
|
|
{
|
|
channelNick = new ChannelNick(nickInfo, false, false, false, false, false);
|
|
Q_ASSERT(channelNick);
|
|
channel->insert(lcNickname, channelNick);
|
|
doChannelMembersChangedSignal = true;
|
|
}
|
|
channelNick = (*channel)[lcNickname];
|
|
Q_ASSERT(channelNick); //Since we just added it if it didn't exist, it should be guaranteed to exist now
|
|
if (doWatchedNickChangedSignal) emit watchedNickChanged(this, nickname, true);
|
|
if (doChannelJoinedSignal) emit channelJoinedOrUnjoined(this, channelName, true);
|
|
if (doChannelMembersChangedSignal) emit channelMembersChanged(this, channelName, true, false, nickname);
|
|
return channelNick;
|
|
}
|
|
|
|
/** This function should _only_ be called from the ChannelNick class.
|
|
* This function should also be the only one to emit this signal.
|
|
* In this class, when channelNick is changed, it emits its own signal, and
|
|
* calls this function itself.
|
|
*/
|
|
void Server::emitChannelNickChanged(const ChannelNickPtr channelNick)
|
|
{
|
|
emit channelNickChanged(this, channelNick);
|
|
}
|
|
|
|
/** This function should _only_ be called from the NickInfo class.
|
|
* This function should also be the only one to emit this signal.
|
|
* In this class, when nickInfo is changed, it emits its own signal, and
|
|
* calls this function itself.
|
|
*/
|
|
void Server::emitNickInfoChanged(const NickInfoPtr nickInfo)
|
|
{
|
|
emit nickInfoChanged(this, nickInfo);
|
|
}
|
|
|
|
// Adds a nickname to the unjoinedChannels list.
|
|
// Creates new NickInfo if necessary.
|
|
// If needed, moves the channel from the joined list to the unjoined list.
|
|
// If mode != 99 sets the mode for this nick in this channel.
|
|
// Returns the NickInfo for the nickname.
|
|
ChannelNickPtr Server::addNickToUnjoinedChannelsList(const TQString& channelName, const TQString& nickname)
|
|
{
|
|
bool doChannelUnjoinedSignal = false;
|
|
bool doWatchedNickChangedSignal = false;
|
|
bool doChannelMembersChangedSignal = false;
|
|
TQString lcNickname = nickname.lower();
|
|
// Create NickInfo if not already created.
|
|
NickInfoPtr nickInfo = getNickInfo(nickname);
|
|
if (!nickInfo)
|
|
{
|
|
nickInfo = new NickInfo(nickname, this);
|
|
m_allNicks.insert(lcNickname, nickInfo);
|
|
doWatchedNickChangedSignal = isWatchedNick(nickname);
|
|
}
|
|
// Move the channel from joined list (if present) to unjoined list.
|
|
TQString lcChannelName = channelName.lower();
|
|
ChannelNickMap *channel;
|
|
if (m_joinedChannels.contains(lcChannelName))
|
|
{
|
|
channel = m_joinedChannels[lcChannelName];
|
|
m_joinedChannels.remove(lcChannelName);
|
|
m_unjoinedChannels.insert(lcChannelName, channel);
|
|
doChannelUnjoinedSignal = true;
|
|
}
|
|
else
|
|
{
|
|
// Create a new list in the unjoined channels if not already present.
|
|
if (!m_unjoinedChannels.contains(lcChannelName))
|
|
{
|
|
channel = new ChannelNickMap;
|
|
m_unjoinedChannels.insert(lcChannelName, channel);
|
|
doChannelUnjoinedSignal = true;
|
|
}
|
|
else
|
|
channel = m_unjoinedChannels[lcChannelName];
|
|
}
|
|
// Add NickInfo to unjoinedChannels list if not already in the list.
|
|
ChannelNickPtr channelNick;
|
|
if (!channel->contains(lcNickname))
|
|
{
|
|
channelNick = new ChannelNick(nickInfo, false, false, false, false, false);
|
|
channel->insert(lcNickname, channelNick);
|
|
doChannelMembersChangedSignal = true;
|
|
}
|
|
channelNick = (*channel)[lcNickname];
|
|
// Set the mode for the nick in this channel.
|
|
if (doWatchedNickChangedSignal) emit watchedNickChanged(this, nickname, true);
|
|
if (doChannelUnjoinedSignal) emit channelJoinedOrUnjoined(this, channelName, false);
|
|
if (doChannelMembersChangedSignal) emit channelMembersChanged(this, channelName, false, false, nickname);
|
|
return channelNick;
|
|
}
|
|
|
|
/**
|
|
* If not already online, changes a nick to the online state by creating
|
|
* a NickInfo for it and emits various signals and messages for it.
|
|
* This method should only be called for nicks on the watch list.
|
|
* @param nickname The nickname that is online.
|
|
* @return Pointer to NickInfo for nick.
|
|
*/
|
|
NickInfoPtr Server::setWatchedNickOnline(const TQString& nickname)
|
|
{
|
|
NickInfoPtr nickInfo = getNickInfo(nickname);
|
|
if (!nickInfo)
|
|
{
|
|
TQString lcNickname = nickname.lower();
|
|
nickInfo = new NickInfo(nickname, this);
|
|
m_allNicks.insert(lcNickname, nickInfo);
|
|
}
|
|
|
|
emit watchedNickChanged(this, nickname, true);
|
|
KABC::Addressee addressee = nickInfo->getAddressee();
|
|
if (!addressee.isEmpty()) Konversation::Addressbook::self()->emitContactPresenceChanged(addressee.uid());
|
|
|
|
appendMessageToFrontmost(i18n("Notify"),"<a href=\"#"+nickname+"\">"+
|
|
i18n("%1 is online (%2).").arg(nickname).arg(getServerName())+"</a>", getStatusView());
|
|
|
|
static_cast<KonversationApplication*>(kapp)->notificationHandler()->nickOnline(getStatusView(), nickname);
|
|
|
|
nickInfo->setPrintedOnline(true);
|
|
return nickInfo;
|
|
}
|
|
|
|
void Server::setWatchedNickOffline(const TQString& nickname, const NickInfoPtr nickInfo)
|
|
{
|
|
if (nickInfo) {
|
|
KABC::Addressee addressee = nickInfo->getAddressee();
|
|
if (!addressee.isEmpty()) Konversation::Addressbook::self()->emitContactPresenceChanged(addressee.uid(), 1);
|
|
}
|
|
|
|
emit watchedNickChanged(this, nickname, false);
|
|
|
|
appendMessageToFrontmost(i18n("Notify"), i18n("%1 went offline (%2).").arg(nickname).arg(getServerName()), getStatusView());
|
|
|
|
static_cast<KonversationApplication*>(kapp)->notificationHandler()->nickOffline(getStatusView(), nickname);
|
|
|
|
}
|
|
|
|
bool Server::setNickOffline(const TQString& nickname)
|
|
{
|
|
TQString lcNickname = nickname.lower();
|
|
NickInfoPtr nickInfo = getNickInfo(lcNickname);
|
|
bool wasOnline = nickInfo->getPrintedOnline();
|
|
|
|
if (nickInfo && wasOnline)
|
|
{
|
|
// Delete from query list, if present.
|
|
if (m_queryNicks.contains(lcNickname)) m_queryNicks.remove(lcNickname);
|
|
// Delete the nickname from all channels (joined or unjoined).
|
|
TQStringList nickChannels = getNickChannels(lcNickname);
|
|
TQStringList::iterator itEnd = nickChannels.end();
|
|
|
|
for(TQStringList::iterator it = nickChannels.begin(); it != itEnd; ++it)
|
|
{
|
|
TQString channel = (*it);
|
|
removeChannelNick(channel, lcNickname);
|
|
}
|
|
|
|
// Delete NickInfo.
|
|
if (m_allNicks.contains(lcNickname)) m_allNicks.remove(lcNickname);
|
|
// If the nick was in the watch list, emit various signals and messages.
|
|
if (isWatchedNick(nickname)) setWatchedNickOffline(nickname, nickInfo);
|
|
|
|
nickInfo->setPrintedOnline(false);
|
|
}
|
|
|
|
return (nickInfo != 0);
|
|
}
|
|
|
|
/**
|
|
* If nickname is no longer on any channel list, or the query list, delete it altogether.
|
|
* Call this routine only if the nick is not on the notify list or is on the notify
|
|
* list but is known to be offline.
|
|
* @param nickname The nickname to be deleted. Case insensitive.
|
|
* @return True if the nickname is deleted.
|
|
*/
|
|
bool Server::deleteNickIfUnlisted(const TQString &nickname)
|
|
{
|
|
TQString lcNickname = nickname.lower();
|
|
// Don't delete our own nickinfo.
|
|
if (lcNickname == loweredNickname()) return false;
|
|
|
|
if (!m_queryNicks.contains(lcNickname))
|
|
{
|
|
TQStringList nickChannels = getNickChannels(nickname);
|
|
if (nickChannels.isEmpty())
|
|
{
|
|
m_allNicks.remove(lcNickname);
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Remove nickname from a channel (on joined or unjoined lists).
|
|
* @param channelName The channel name. Case insensitive.
|
|
* @param nickname The nickname. Case insensitive.
|
|
*/
|
|
void Server::removeChannelNick(const TQString& channelName, const TQString& nickname)
|
|
{
|
|
bool doSignal = false;
|
|
bool joined = false;
|
|
TQString lcChannelName = channelName.lower();
|
|
TQString lcNickname = nickname.lower();
|
|
ChannelNickMap *channel;
|
|
if (m_joinedChannels.contains(lcChannelName))
|
|
{
|
|
channel = m_joinedChannels[lcChannelName];
|
|
if (channel->contains(lcNickname))
|
|
{
|
|
channel->remove(lcNickname);
|
|
doSignal = true;
|
|
joined = true;
|
|
// Note: Channel should not be empty because user's own nick should still be
|
|
// in it, so do not need to delete empty channel here.
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (m_unjoinedChannels.contains(lcChannelName))
|
|
{
|
|
channel = m_unjoinedChannels[lcChannelName];
|
|
if (channel->contains(lcNickname))
|
|
{
|
|
channel->remove(lcNickname);
|
|
doSignal = true;
|
|
joined = false;
|
|
// If channel is now empty, delete it.
|
|
// Caution: Any iterators across unjoinedChannels will be come invalid here.
|
|
if (channel->isEmpty()) m_unjoinedChannels.remove(lcChannelName);
|
|
}
|
|
}
|
|
}
|
|
if (doSignal) emit channelMembersChanged(this, channelName, joined, true, nickname);
|
|
}
|
|
|
|
TQStringList Server::getWatchList()
|
|
{
|
|
// no nickinfo ISON for the time being
|
|
return Preferences::notifyListByGroupName(getDisplayName());
|
|
if (m_serverISON)
|
|
return m_serverISON->getWatchList();
|
|
else
|
|
return TQStringList();
|
|
}
|
|
|
|
TQString Server::getWatchListString() { return getWatchList().join(" "); }
|
|
|
|
TQStringList Server::getISONList()
|
|
{
|
|
// no nickinfo ISON for the time being
|
|
return Preferences::notifyListByGroupName(getDisplayName());
|
|
if (m_serverISON)
|
|
return m_serverISON->getISONList();
|
|
else
|
|
return TQStringList();
|
|
}
|
|
|
|
TQString Server::getISONListString() { return getISONList().join(" "); }
|
|
|
|
/**
|
|
* Return true if the given nickname is on the watch list.
|
|
*/
|
|
bool Server::isWatchedNick(const TQString& nickname)
|
|
{
|
|
// Get watch list from preferences.
|
|
TQString watchlist= ' ' + getWatchListString() + ' ';
|
|
// Search case-insensitivly
|
|
return (watchlist.find(' ' + nickname + ' ', 0, 0) != -1);
|
|
}
|
|
|
|
/**
|
|
* Remove channel from the joined list, placing it in the unjoined list.
|
|
* All the unwatched nicks are removed from the channel. If the channel becomes
|
|
* empty, it is deleted.
|
|
* @param channelName Name of the channel. Case sensitive.
|
|
*/
|
|
void Server::removeJoinedChannel(const TQString& channelName)
|
|
{
|
|
bool doSignal = false;
|
|
TQStringList watchListLower = getWatchList();
|
|
TQString lcChannelName = channelName.lower();
|
|
// Move the channel nick list from the joined to unjoined lists.
|
|
if (m_joinedChannels.contains(lcChannelName))
|
|
{
|
|
doSignal = true;
|
|
ChannelNickMap* channel = m_joinedChannels[lcChannelName];
|
|
m_joinedChannels.remove(lcChannelName);
|
|
m_unjoinedChannels.insert(lcChannelName, channel);
|
|
// Remove nicks not on the watch list.
|
|
bool allDeleted = true;
|
|
Q_ASSERT(channel);
|
|
if(!channel) return; //already removed.. hmm
|
|
ChannelNickMap::Iterator member;
|
|
for ( member = channel->begin(); member != channel->end() ;)
|
|
{
|
|
TQString lcNickname = member.key();
|
|
if (watchListLower.find(lcNickname) == watchListLower.end())
|
|
{
|
|
// Remove the unwatched nickname from the unjoined channel.
|
|
channel->remove(member);
|
|
// If the nick is no longer listed in any channels or query list, delete it altogether.
|
|
deleteNickIfUnlisted(lcNickname);
|
|
member = channel->begin();
|
|
}
|
|
else
|
|
{
|
|
allDeleted = false;
|
|
++member;
|
|
}
|
|
}
|
|
// If all were deleted, remove the channel from the unjoined list.
|
|
if (allDeleted)
|
|
{
|
|
channel = m_unjoinedChannels[lcChannelName];
|
|
m_unjoinedChannels.remove(lcChannelName);
|
|
delete channel; // recover memory!
|
|
}
|
|
}
|
|
if (doSignal) emit channelJoinedOrUnjoined(this, channelName, false);
|
|
}
|
|
|
|
// Renames a nickname in all NickInfo lists.
|
|
// Returns pointer to the NickInfo object or 0 if nick not found.
|
|
void Server::renameNickInfo(NickInfoPtr nickInfo, const TQString& newname)
|
|
{
|
|
if (nickInfo)
|
|
{
|
|
// Get existing lowercase nickname and rename nickname in the NickInfo object.
|
|
TQString lcNickname = nickInfo->loweredNickname();
|
|
nickInfo->setNickname(newname);
|
|
nickInfo->setIdentified(false);
|
|
TQString lcNewname = newname.lower();
|
|
// Rename the key in m_allNicks list.
|
|
m_allNicks.remove(lcNickname);
|
|
m_allNicks.insert(lcNewname, nickInfo);
|
|
// Rename key in the joined and unjoined lists.
|
|
TQStringList nickChannels = getNickChannels(lcNickname);
|
|
TQStringList::iterator itEnd = nickChannels.end();
|
|
|
|
for(TQStringList::iterator it = nickChannels.begin(); it != itEnd; ++it)
|
|
{
|
|
const ChannelNickMap *channel = getChannelMembers(*it);
|
|
Q_ASSERT(channel);
|
|
ChannelNickPtr member = (*channel)[lcNickname];
|
|
Q_ASSERT(member);
|
|
const_cast<ChannelNickMap *>(channel)->remove(lcNickname);
|
|
const_cast<ChannelNickMap *>(channel)->insert(lcNewname, member);
|
|
}
|
|
|
|
// Rename key in Query list.
|
|
if (m_queryNicks.contains(lcNickname))
|
|
{
|
|
m_queryNicks.remove(lcNickname);
|
|
m_queryNicks.insert(lcNewname, nickInfo);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
kdDebug() << "server::renameNickInfo() was called for newname='" << newname << "' but nickInfo is null" << endl;
|
|
}
|
|
}
|
|
|
|
Channel* Server::nickJoinsChannel(const TQString &channelName, const TQString &nickname, const TQString &hostmask)
|
|
{
|
|
Channel* outChannel=getChannelByName(channelName);
|
|
if(outChannel)
|
|
{
|
|
// Update NickInfo.
|
|
ChannelNickPtr channelNick = addNickToJoinedChannelsList(channelName, nickname);
|
|
NickInfoPtr nickInfo = channelNick->getNickInfo();
|
|
if ((nickInfo->getHostmask() != hostmask) && !hostmask.isEmpty())
|
|
{
|
|
nickInfo->setHostmask(hostmask);
|
|
}
|
|
outChannel->joinNickname(channelNick);
|
|
}
|
|
|
|
return outChannel;
|
|
}
|
|
|
|
void Server::addHostmaskToNick(const TQString& sourceNick, const TQString& sourceHostmask)
|
|
{
|
|
// Update NickInfo.
|
|
NickInfoPtr nickInfo=getNickInfo(sourceNick);
|
|
if (nickInfo)
|
|
{
|
|
if ((nickInfo->getHostmask() != sourceHostmask) && !sourceHostmask.isEmpty())
|
|
{
|
|
nickInfo->setHostmask(sourceHostmask);
|
|
}
|
|
}
|
|
}
|
|
|
|
Channel* Server::removeNickFromChannel(const TQString &channelName, const TQString &nickname, const TQString &reason, bool quit)
|
|
{
|
|
Channel* outChannel=getChannelByName(channelName);
|
|
if(outChannel)
|
|
{
|
|
ChannelNickPtr channelNick = getChannelNick(channelName, nickname);
|
|
if(channelNick) outChannel->removeNick(channelNick,reason,quit);
|
|
}
|
|
|
|
// Remove the nick from the channel.
|
|
removeChannelNick(channelName, nickname);
|
|
// If not listed in any channel, and not on query list, delete the NickInfo,
|
|
// but only if not on the notify list. ISON replies will take care of deleting
|
|
// the NickInfo, if on the notify list.
|
|
if (!isWatchedNick(nickname))
|
|
{
|
|
TQString nicky = nickname;
|
|
deleteNickIfUnlisted(nicky);
|
|
}
|
|
|
|
return outChannel;
|
|
}
|
|
|
|
void Server::nickWasKickedFromChannel(const TQString &channelName, const TQString &nickname, const TQString &kicker, const TQString &reason)
|
|
{
|
|
Channel* outChannel=getChannelByName(channelName);
|
|
if(outChannel)
|
|
{
|
|
ChannelNickPtr channelNick = getChannelNick(channelName, nickname);
|
|
|
|
if(channelNick)
|
|
{
|
|
outChannel->kickNick(channelNick, kicker, reason);
|
|
// Tell Nickinfo
|
|
removeChannelNick(channelName,nickname);
|
|
}
|
|
}
|
|
}
|
|
|
|
void Server::removeNickFromServer(const TQString &nickname,const TQString &reason)
|
|
{
|
|
Channel* channel = m_channelList.first();
|
|
while (channel)
|
|
{
|
|
// Check if nick is in this channel or not.
|
|
if(channel->getNickByName(nickname))
|
|
removeNickFromChannel(channel->getName(),nickname,reason,true);
|
|
|
|
channel = m_channelList.next();
|
|
}
|
|
|
|
Query* query=getQueryByName(nickname);
|
|
if (query) query->quitNick(reason);
|
|
|
|
// Delete the nick from all channels and then delete the nickinfo,
|
|
// emitting signal if on the watch list.
|
|
setNickOffline(nickname);
|
|
}
|
|
|
|
void Server::renameNick(const TQString &nickname, const TQString &newNick)
|
|
{
|
|
if(nickname.isEmpty() || newNick.isEmpty())
|
|
{
|
|
kdDebug() << "server::renameNick called with empty strings! Trying to rename '" << nickname << "' to '" << newNick << "'" << endl;
|
|
return;
|
|
}
|
|
|
|
// If this was our own nickchange, tell our server object about it
|
|
if (nickname == getNickname())
|
|
{
|
|
setNickname(newNick);
|
|
|
|
// We may get a request from nickserv, so remove the auto-identify lock.
|
|
m_autoIdentifyLock = false;
|
|
}
|
|
|
|
//Actually do the rename.
|
|
NickInfoPtr nickInfo = getNickInfo(nickname);
|
|
|
|
if(!nickInfo)
|
|
{
|
|
kdDebug() << "server::renameNick called for nickname '" << nickname << "' to '" << newNick << "' but getNickInfo('" << nickname << "') returned no results." << endl;
|
|
}
|
|
else
|
|
{
|
|
renameNickInfo(nickInfo, newNick);
|
|
//The rest of the code below allows the channels to echo to the user to tell them that the nick has changed.
|
|
|
|
// Rename the nick in every channel they are in
|
|
Channel* channel=m_channelList.first();
|
|
while (channel)
|
|
{
|
|
// All we do is notify that the nick has been renamed.. we haven't actually renamed it yet
|
|
// Note that NickPanel has already updated, so pass new nick to getNickByName.
|
|
if (channel->getNickByName(newNick)) channel->nickRenamed(nickname, *nickInfo);
|
|
channel = m_channelList.next();
|
|
}
|
|
//Watched nicknames stuff
|
|
if (isWatchedNick(nickname)) setWatchedNickOffline(nickname, 0);
|
|
}
|
|
// If we had a query with this nick, change that name, too
|
|
|
|
}
|
|
|
|
void Server::userhost(const TQString& nick,const TQString& hostmask,bool away,bool /* ircOp */)
|
|
{
|
|
addHostmaskToNick(nick, hostmask);
|
|
// remember my IP for DCC things
|
|
// myself
|
|
if (m_ownIpByUserhost.isEmpty() && nick == getNickname())
|
|
{
|
|
TQString myhost = hostmask.section('@', 1);
|
|
// Use async lookup else you will be blocking GUI badly
|
|
KNetwork::KResolver::resolveAsync(this,TQT_SLOT(gotOwnResolvedHostByUserhost(KResolverResults)),myhost,"0");
|
|
}
|
|
NickInfoPtr nickInfo = getNickInfo(nick);
|
|
if (nickInfo)
|
|
{
|
|
if (nickInfo->isAway() != away)
|
|
{
|
|
nickInfo->setAway(away);
|
|
}
|
|
}
|
|
}
|
|
|
|
void Server::gotOwnResolvedHostByUserhost(KResolverResults res)
|
|
{
|
|
if ( res.error() == KResolver::NoError && !res.isEmpty() )
|
|
m_ownIpByUserhost = res.first().address().nodeName();
|
|
else
|
|
kdDebug() << "Server::gotOwnResolvedHostByUserhost(): Got error: " << ( int )res.error() << endl;
|
|
}
|
|
|
|
void Server::appendServerMessageToChannel(const TQString& channel,const TQString& type,const TQString& message)
|
|
{
|
|
Channel* outChannel = getChannelByName(channel);
|
|
if (outChannel) outChannel->appendServerMessage(type,message);
|
|
}
|
|
|
|
void Server::appendCommandMessageToChannel(const TQString& channel,const TQString& command,const TQString& message, bool highlight)
|
|
{
|
|
Channel* outChannel = getChannelByName(channel);
|
|
if (outChannel)
|
|
{
|
|
outChannel->appendCommandMessage(command,message,true,true,!highlight);
|
|
}
|
|
else
|
|
{
|
|
appendStatusMessage(command, TQString("%1 %2").arg(channel).arg(message));
|
|
}
|
|
}
|
|
|
|
void Server::appendStatusMessage(const TQString& type,const TQString& message)
|
|
{
|
|
getStatusView()->appendServerMessage(type,message);
|
|
}
|
|
|
|
void Server::appendMessageToFrontmost(const TQString& type,const TQString& message, bool parseURL)
|
|
{
|
|
getViewContainer()->appendToFrontmost(type, message, getStatusView(), parseURL);
|
|
}
|
|
|
|
void Server::setNickname(const TQString &newNickname)
|
|
{
|
|
m_nickname = newNickname;
|
|
m_loweredNickname = newNickname.lower();
|
|
emit nicknameChanged(newNickname);
|
|
}
|
|
|
|
void Server::setChannelTopic(const TQString &channel, const TQString &newTopic)
|
|
{
|
|
Channel* outChannel = getChannelByName(channel);
|
|
if(outChannel)
|
|
{
|
|
// encoding stuff is done in send()
|
|
outChannel->setTopic(newTopic);
|
|
}
|
|
}
|
|
|
|
// Overloaded
|
|
void Server::setChannelTopic(const TQString& nickname, const TQString &channel, const TQString &newTopic)
|
|
{
|
|
Channel* outChannel = getChannelByName(channel);
|
|
if(outChannel)
|
|
{
|
|
// encoding stuff is done in send()
|
|
outChannel->setTopic(nickname,newTopic);
|
|
}
|
|
}
|
|
|
|
void Server::setTopicAuthor(const TQString& channel, const TQString& author, TQDateTime time)
|
|
{
|
|
Channel* outChannel = getChannelByName(channel);
|
|
if(outChannel)
|
|
outChannel->setTopicAuthor(author, time);
|
|
}
|
|
|
|
void Server::endOfWho(const TQString& target)
|
|
{
|
|
Channel* channel = getChannelByName(target);
|
|
if(channel)
|
|
channel->scheduleAutoWho();
|
|
}
|
|
|
|
bool Server::isNickname(const TQString &compare) const
|
|
{
|
|
return (m_nickname == compare);
|
|
}
|
|
|
|
TQString Server::getNickname() const
|
|
{
|
|
return m_nickname;
|
|
}
|
|
|
|
TQString Server::loweredNickname() const
|
|
{
|
|
return m_loweredNickname;
|
|
}
|
|
|
|
TQString Server::parseWildcards(const TQString& toParse,
|
|
const TQString& sender,
|
|
const TQString& channelName,
|
|
const TQString& channelKey,
|
|
const TQString& nick,
|
|
const TQString& parameter)
|
|
{
|
|
return parseWildcards(toParse,sender,channelName,channelKey,TQStringList::split(' ',nick),parameter);
|
|
}
|
|
|
|
TQString Server::parseWildcards(const TQString& toParse,
|
|
const TQString& sender,
|
|
const TQString& channelName,
|
|
const TQString& channelKey,
|
|
const TQStringList& nickList,
|
|
const TQString& /*parameter*/)
|
|
{
|
|
// TODO: parameter handling, since parameters are not functional yet
|
|
|
|
// store the parsed version
|
|
TQString out;
|
|
|
|
// default separator
|
|
TQString separator(" ");
|
|
|
|
int index = 0, found = 0;
|
|
TQChar toExpand;
|
|
|
|
while ((found = toParse.find('%',index)) != -1)
|
|
{
|
|
// append part before the %
|
|
out.append(toParse.mid(index,found-index));
|
|
index = found + 1; // skip the part before, including %
|
|
if (index >= (int)toParse.length())
|
|
break; // % was the last char (not valid)
|
|
toExpand = toParse.at(index++);
|
|
if (toExpand == 's')
|
|
{
|
|
found = toParse.find('%',index);
|
|
if (found == -1) // no other % (not valid)
|
|
break;
|
|
separator = toParse.mid(index,found-index);
|
|
index = found + 1; // skip separator, including %
|
|
}
|
|
else if (toExpand == 'u')
|
|
{
|
|
out.append(nickList.join(separator));
|
|
}
|
|
else if (toExpand == 'c')
|
|
{
|
|
if(!channelName.isEmpty())
|
|
out.append(channelName);
|
|
}
|
|
else if (toExpand == 'o')
|
|
{
|
|
out.append(sender);
|
|
}
|
|
else if (toExpand == 'k')
|
|
{
|
|
if(!channelKey.isEmpty())
|
|
out.append(channelKey);
|
|
}
|
|
else if (toExpand == 'K')
|
|
{
|
|
if(getConnectionSettings().server().password().isEmpty())
|
|
out.append(getConnectionSettings().server().password());
|
|
}
|
|
else if (toExpand == 'n')
|
|
{
|
|
out.append("\n");
|
|
}
|
|
else if (toExpand == 'p')
|
|
{
|
|
out.append("%");
|
|
}
|
|
}
|
|
|
|
// append last part
|
|
out.append(toParse.mid(index,toParse.length()-index));
|
|
return out;
|
|
}
|
|
|
|
void Server::sendToAllChannels(const TQString &text)
|
|
{
|
|
// Send a message to all channels we are in
|
|
Channel* channel = m_channelList.first();
|
|
while (channel)
|
|
{
|
|
channel->sendChannelText(text);
|
|
channel = m_channelList.next();
|
|
}
|
|
}
|
|
|
|
void Server::invitation(const TQString& nick,const TQString& channel)
|
|
{
|
|
if(KMessageBox::questionYesNo(getViewContainer()->getWindow(),
|
|
i18n("You were invited by %1 to join channel %2. "
|
|
"Do you accept the invitation?").arg(nick).arg(channel),
|
|
i18n("Invitation"),
|
|
i18n("Join"),
|
|
i18n("Ignore"),
|
|
"Invitation")==KMessageBox::Yes)
|
|
{
|
|
sendJoinCommand(channel);
|
|
}
|
|
}
|
|
|
|
void Server::scriptNotFound(const TQString& name)
|
|
{
|
|
appendMessageToFrontmost(i18n("DCOP"),i18n("Error: Could not find script \"%1\".").arg(name));
|
|
}
|
|
|
|
void Server::scriptExecutionError(const TQString& name)
|
|
{
|
|
appendMessageToFrontmost(i18n("DCOP"),i18n("Error: Could not execute script \"%1\". Check file permissions.").arg(name));
|
|
}
|
|
|
|
bool Server::isAChannel(const TQString &channel) const
|
|
{
|
|
return (getChannelTypes().contains(channel.at(0)) > 0);
|
|
}
|
|
|
|
void Server::addRawLog(bool show)
|
|
{
|
|
if (!m_rawLog) m_rawLog = getViewContainer()->addRawLog(this);
|
|
|
|
connect(this, TQT_SIGNAL(serverOnline(bool)), m_rawLog, TQT_SLOT(serverOnline(bool)));
|
|
|
|
// bring raw log to front since the main window does not do this for us
|
|
if (show) emit showView(m_rawLog);
|
|
}
|
|
|
|
void Server::closeRawLog()
|
|
{
|
|
if (m_rawLog) delete m_rawLog;
|
|
}
|
|
|
|
ChannelListPanel* Server::addChannelListPanel()
|
|
{
|
|
if(!m_channelListPanel)
|
|
{
|
|
m_channelListPanel = getViewContainer()->addChannelListPanel(this);
|
|
|
|
connect(m_channelListPanel, TQT_SIGNAL(refreshChannelList()), this, TQT_SLOT(requestChannelList()));
|
|
connect(m_channelListPanel, TQT_SIGNAL(joinChannel(const TQString&)), this, TQT_SLOT(sendJoinCommand(const TQString&)));
|
|
connect(this, TQT_SIGNAL(serverOnline(bool)), m_channelListPanel, TQT_SLOT(serverOnline(bool)));
|
|
}
|
|
|
|
return m_channelListPanel;
|
|
}
|
|
|
|
void Server::addToChannelList(const TQString& channel, int users, const TQString& topic)
|
|
{
|
|
addChannelListPanel();
|
|
m_channelListPanel->addToChannelList(channel, users, topic);
|
|
}
|
|
|
|
ChannelListPanel* Server::getChannelListPanel() const
|
|
{
|
|
return m_channelListPanel;
|
|
}
|
|
|
|
void Server::closeChannelListPanel()
|
|
{
|
|
if (m_channelListPanel) delete m_channelListPanel;
|
|
}
|
|
|
|
void Server::updateAutoJoin(Konversation::ChannelSettings channel)
|
|
{
|
|
if (!channel.name().isEmpty())
|
|
{
|
|
setAutoJoin(true);
|
|
|
|
setAutoJoinCommands(TQStringList("JOIN " + channel.name() + " " + channel.password()));
|
|
|
|
return;
|
|
}
|
|
|
|
Konversation::ChannelList tmpList;
|
|
|
|
if (m_channelList.isEmpty() && getServerGroup())
|
|
tmpList = getServerGroup()->channelList();
|
|
else
|
|
{
|
|
TQPtrListIterator<Channel> it(m_channelList);
|
|
Channel* channel;
|
|
|
|
while ((channel = it.current()) != 0)
|
|
{
|
|
++it;
|
|
tmpList << channel->channelSettings();
|
|
}
|
|
}
|
|
|
|
if (!tmpList.isEmpty())
|
|
{
|
|
setAutoJoin(true);
|
|
|
|
TQStringList channels;
|
|
TQStringList passwords;
|
|
TQStringList joinCommands;
|
|
uint length = 0;
|
|
|
|
Konversation::ChannelList::iterator it;
|
|
|
|
for (it = tmpList.begin(); it != tmpList.end(); ++it)
|
|
{
|
|
TQString channel = (*it).name();;
|
|
TQString password = ((*it).password().isEmpty() ? "." : (*it).password());
|
|
|
|
int tempLen = channel.length();
|
|
length += getIdentity()->getCodec()->fromUnicode(channel, tempLen).length();
|
|
tempLen = password.length();
|
|
length += getIdentity()->getCodec()->fromUnicode(password, tempLen).length();
|
|
|
|
if (length + 6 < 512) // 6: "JOIN " plus separating space between chans and pws.
|
|
{
|
|
channels << channel;
|
|
passwords << password;
|
|
}
|
|
else
|
|
{
|
|
if (passwords.last() == ".") passwords.pop_back();
|
|
|
|
joinCommands << "JOIN " + channels.join(",") + " " + passwords.join(",");
|
|
|
|
channels.clear();
|
|
passwords.clear();
|
|
|
|
channels << channel;
|
|
passwords << password;
|
|
|
|
length = 0;
|
|
|
|
tempLen = channel.length();
|
|
length += getIdentity()->getCodec()->fromUnicode(channel, tempLen).length();
|
|
tempLen = password.length();
|
|
length += getIdentity()->getCodec()->fromUnicode(password, tempLen).length();
|
|
}
|
|
}
|
|
|
|
if (passwords.last() == ".") passwords.pop_back();
|
|
|
|
joinCommands << "JOIN " + channels.join(",") + " " + passwords.join(",");
|
|
|
|
setAutoJoinCommands(joinCommands);
|
|
}
|
|
else
|
|
setAutoJoin(false);
|
|
}
|
|
|
|
ViewContainer* Server::getViewContainer() const
|
|
{
|
|
KonversationApplication* konvApp = static_cast<KonversationApplication *>(kapp);
|
|
return konvApp->getMainWindow()->getViewContainer();
|
|
}
|
|
|
|
bool Server::getUseSSL() const
|
|
{
|
|
SSLSocket* sslsocket = dynamic_cast<SSLSocket*>(m_socket);
|
|
|
|
return (sslsocket != 0);
|
|
}
|
|
|
|
TQString Server::getSSLInfo() const
|
|
{
|
|
SSLSocket* sslsocket = dynamic_cast<SSLSocket*>(m_socket);
|
|
|
|
if(sslsocket)
|
|
return sslsocket->details();
|
|
|
|
return TQString();
|
|
}
|
|
|
|
void Server::sendMultiServerCommand(const TQString& command, const TQString& parameter)
|
|
{
|
|
emit multiServerCommand(command, parameter);
|
|
}
|
|
|
|
void Server::executeMultiServerCommand(const TQString& command, const TQString& parameter)
|
|
{
|
|
if (command == "msg")
|
|
sendToAllChannelsAndQueries(parameter);
|
|
else
|
|
sendToAllChannelsAndQueries(Preferences::commandChar() + command + ' ' + parameter);
|
|
}
|
|
|
|
void Server::sendToAllChannelsAndQueries(const TQString& text)
|
|
{
|
|
// Send a message to all channels we are in
|
|
Channel* channel = m_channelList.first();
|
|
|
|
while (channel)
|
|
{
|
|
channel->sendChannelText(text);
|
|
channel = m_channelList.next();
|
|
}
|
|
|
|
// Send a message to all queries we are in
|
|
class Query* query = m_queryList.first();
|
|
|
|
while (query)
|
|
{
|
|
query->sendQueryText(text);
|
|
query = m_queryList.next();
|
|
}
|
|
}
|
|
|
|
bool Server::isSocketConnected() const
|
|
{
|
|
if (!m_socket) return false;
|
|
|
|
return (m_socket->state() == KNetwork::KClientSocketBase::Connected);
|
|
}
|
|
|
|
void Server::updateConnectionState(Konversation::ConnectionState state)
|
|
{
|
|
if (state != m_connectionState)
|
|
{
|
|
m_connectionState = state;
|
|
|
|
if (m_connectionState == Konversation::SSConnected)
|
|
emit serverOnline(true);
|
|
else if (m_connectionState != Konversation::SSConnecting)
|
|
emit serverOnline(false);
|
|
|
|
emit connectionStateChanged(this, state);
|
|
}
|
|
}
|
|
|
|
void Server::reconnect()
|
|
{
|
|
if (isConnecting() || isSocketConnected()) quitServer();
|
|
|
|
// Use asynchronous invocation so that the broken() that the above
|
|
// quitServer might cause is delivered before connectToIRCServer
|
|
// sets SSConnecting and broken() announces a deliberate disconnect
|
|
// due to the failure allegedly occuring during SSConnecting.
|
|
TQTimer::singleShot(0, this, TQT_SLOT(connectToIRCServer()));
|
|
}
|
|
|
|
void Server::disconnect()
|
|
{
|
|
if (isSocketConnected()) quitServer();
|
|
}
|
|
|
|
void Server::requestAway(const TQString& reason)
|
|
{
|
|
TQString awayReason = reason;
|
|
IdentityPtr identity = getIdentity();
|
|
|
|
if (awayReason.isEmpty() || !identity)
|
|
awayReason = i18n("Gone away for now");
|
|
|
|
setAwayReason(awayReason);
|
|
|
|
queue("AWAY :" + awayReason);
|
|
}
|
|
|
|
void Server::requestUnaway()
|
|
{
|
|
queue("AWAY");
|
|
}
|
|
|
|
void Server::setAway(bool away)
|
|
{
|
|
IdentityPtr identity = getIdentity();
|
|
|
|
if (away)
|
|
{
|
|
if (!m_away) startAwayTimer();
|
|
|
|
m_away = true;
|
|
|
|
emit awayState(true);
|
|
|
|
if (identity && !identity->getAwayNick().isEmpty() && identity->getAwayNick() != getNickname())
|
|
{
|
|
m_nonAwayNick = getNickname();
|
|
queue("NICK " + getIdentity()->getAwayNick());
|
|
}
|
|
|
|
appendMessageToFrontmost(i18n("Away"), i18n("You are now marked as being away."));
|
|
|
|
if (identity && identity->getShowAwayMessage())
|
|
{
|
|
TQString message = identity->getAwayMessage();
|
|
sendToAllChannels(message.replace(TQRegExp("%s", false), m_awayReason));
|
|
}
|
|
|
|
if (identity && identity->getInsertRememberLineOnAway())
|
|
emit awayInsertRememberLine(this);
|
|
}
|
|
else
|
|
{
|
|
m_awayReason = TQString();
|
|
|
|
emit awayState(false);
|
|
|
|
if (!identity->getAwayNick().isEmpty() && !m_nonAwayNick.isEmpty())
|
|
{
|
|
queue("NICK " + m_nonAwayNick);
|
|
m_nonAwayNick = "";
|
|
}
|
|
|
|
if (m_away)
|
|
{
|
|
appendMessageToFrontmost(i18n("Away"), i18n("You are no longer marked as being away."));
|
|
|
|
if (identity && identity->getShowAwayMessage())
|
|
{
|
|
TQString message = identity->getReturnMessage();
|
|
sendToAllChannels(message.replace(TQRegExp("%t", false), awayTime()));
|
|
}
|
|
}
|
|
else
|
|
appendMessageToFrontmost(i18n("Away"), i18n("You are not marked as being away."));
|
|
|
|
m_away = false;
|
|
}
|
|
}
|
|
|
|
TQString Server::awayTime() const
|
|
{
|
|
TQString retVal;
|
|
|
|
if (m_away)
|
|
{
|
|
int diff = TQDateTime::currentDateTime().toTime_t() - m_awayTime;
|
|
int num = diff / 3600;
|
|
|
|
if (num < 10)
|
|
retVal = '0' + TQString::number(num) + ':';
|
|
else
|
|
retVal = TQString::number(num) + ':';
|
|
|
|
num = (diff % 3600) / 60;
|
|
|
|
if (num < 10) retVal += '0';
|
|
|
|
retVal += TQString::number(num) + ':';
|
|
|
|
num = (diff % 3600) % 60;
|
|
|
|
if (num < 10) retVal += '0';
|
|
|
|
retVal += TQString::number(num);
|
|
}
|
|
else
|
|
retVal = "00:00:00";
|
|
|
|
return retVal;
|
|
}
|
|
|
|
void Server::startAwayTimer()
|
|
{
|
|
m_awayTime = TQDateTime::currentDateTime().toTime_t();
|
|
}
|
|
|
|
KABC::Addressee Server::getOfflineNickAddressee(TQString& nickname)
|
|
{
|
|
if (m_serverISON)
|
|
return m_serverISON->getOfflineNickAddressee(nickname);
|
|
else
|
|
return KABC::Addressee();
|
|
}
|
|
|
|
void Server::enableIdentifyMsg(bool enabled)
|
|
{
|
|
m_identifyMsg = enabled;
|
|
}
|
|
|
|
bool Server::identifyMsgEnabled()
|
|
{
|
|
return m_identifyMsg;
|
|
}
|
|
|
|
void Server::addBan(const TQString &channel, const TQString &ban)
|
|
{
|
|
Channel* outChannel = getChannelByName(channel);
|
|
if(outChannel)
|
|
{
|
|
outChannel->addBan(ban);
|
|
}
|
|
}
|
|
|
|
void Server::removeBan(const TQString &channel, const TQString &ban)
|
|
{
|
|
Channel* outChannel = getChannelByName(channel);
|
|
if(outChannel)
|
|
{
|
|
outChannel->removeBan(ban);
|
|
}
|
|
}
|
|
|
|
void Server::sendPing()
|
|
{
|
|
//WHO ourselves once a minute in case the irc server has changed our
|
|
//hostmask, such as what happens when a Freenode cloak is activated.
|
|
//It might be more intelligent to only do this when there is text
|
|
//in the inputbox. Kinda changes this into a "do minutely"
|
|
//queue :-)
|
|
TQStringList ql;
|
|
ql << "PING LAG" + TQTime::currentTime().toString("hhmmss");
|
|
getInputFilter()->setAutomaticRequest("WHO", getNickname(), true);
|
|
ql << "WHO " + getNickname();
|
|
queueList(ql, HighPriority);
|
|
|
|
m_lagTime.start();
|
|
m_inputFilter.setLagMeasuring(true);
|
|
m_pingResponseTimer.start(1000 /*1 sec*/);
|
|
}
|
|
|
|
void Server::pongReceived()
|
|
{
|
|
m_currentLag = m_lagTime.elapsed();
|
|
m_inputFilter.setLagMeasuring(false);
|
|
m_pingResponseTimer.stop();
|
|
|
|
emit serverLag(this, m_currentLag);
|
|
|
|
// Send another PING in 60 seconds
|
|
TQTimer::singleShot(60000 /*60 sec*/, this, TQT_SLOT(sendPing()));
|
|
}
|
|
|
|
void Server::updateLongPongLag()
|
|
{
|
|
if (isSocketConnected())
|
|
{
|
|
m_currentLag = m_lagTime.elapsed();
|
|
emit tooLongLag(this, m_currentLag);
|
|
// kdDebug() << "Current lag: " << currentLag << endl;
|
|
|
|
if (m_currentLag > (Preferences::maximumLagTime() * 1000))
|
|
m_socket->close();
|
|
}
|
|
}
|
|
|
|
void Server::updateEncoding()
|
|
{
|
|
if(getViewContainer() && getViewContainer()->getFrontView())
|
|
getViewContainer()->updateViewEncoding(getViewContainer()->getFrontView());
|
|
}
|
|
|
|
#include "server.moc"
|
|
|
|
// kate: space-indent on; tab-width 4; indent-width 4; mixed-indent off; replace-tabs on;
|
|
// vim: set et sw=4 ts=4 cino=l1,cs,U1:
|