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.
konversation/konversation/src/dccchat.cpp

474 lines
14 KiB

/*
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) 2006 Eike Hein <hein@kde.org>
Copyright (C) 2004,2007 Shintaro Matsuoka <shin@shoegazed.org>
*/
#include "dccchat.h"
#include "dcccommon.h"
#include "konversationapplication.h"
#include "konversationmainwindow.h"
#include "irccharsets.h"
#include "ircview.h"
#include "ircviewbox.h"
#include "ircinput.h"
#include "topiclabel.h"
#include "server.h"
#include "channel.h"
#include <stdlib.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <tqvbox.h>
#include <tqhostaddress.h>
#include <tqtextcodec.h>
#include <tqsplitter.h>
#include <tqpopupmenu.h>
#include <klineedit.h>
#include <tdelocale.h>
#include <tdemessagebox.h>
#include <kdebug.h>
#include <kserversocket.h>
#include <tdesocketaddress.h>
#include <kstreamsocket.h>
#include <tdeaction.h>
#define DCCCHAT_BUFFER_SIZE 1024
DccChat::DccChat(TQWidget* parent, bool listen, Server* server, const TQString& ownNick, const TQString& partnerNick, const TQString& partnerHost, int partnerPort)
: ChatWindow(parent)
{
kdDebug() << "DccChat::DccChat() [BEGIN]" << endl;
m_dccSocket = 0;
m_listenSocket = 0;
m_ownNick = ownNick;
m_partnerNick = partnerNick;
m_partnerHost = partnerHost;
m_partnerPort = partnerPort;
setType(ChatWindow::DccChat);
setChannelEncodingSupported(true);
m_headerSplitter = new TQSplitter(Qt::Vertical, this);
m_sourceLine = new Konversation::TopicLabel(m_headerSplitter);
IRCViewBox* ircViewBox = new IRCViewBox(m_headerSplitter, NULL);
setTextView(ircViewBox->ircView());
m_dccChatInput = new IRCInput(this);
getTextView()->installEventFilter(m_dccChatInput);
m_dccChatInput->setEnabled( false );
ChatWindow::setName( '-' + m_partnerNick + '-' );
ChatWindow::setLogfileName( '-' + m_partnerNick + '-' );
TQPopupMenu* popup = textView->getPopup();
if (popup)
{
TDEAction* action = KonversationApplication::instance()->getMainWindow()->actionCollection()->action("open_logfile");
if (action)
{
popup->insertSeparator();
action->plug(popup);
}
}
// connect the signals and slots
connect( m_dccChatInput, TQT_SIGNAL( submit() ), this, TQT_SLOT( dccChatTextEntered() ) );
connect( m_dccChatInput, TQT_SIGNAL( textPasted( const TQString& ) ), this, TQT_SLOT( textPasted( const TQString& ) ) );
connect( getTextView(), TQT_SIGNAL( textPasted(bool) ), m_dccChatInput, TQT_SLOT( paste(bool) ) );
connect( getTextView(), TQT_SIGNAL( gotFocus() ), m_dccChatInput, TQT_SLOT( setFocus() ) );
connect( getTextView(), TQT_SIGNAL( autoText(const TQString&) ), this, TQT_SLOT( sendDccChatText( const TQString& ) ) );
if (listen)
{
listenForPartner();
TQString ownNumericalIp = DccCommon::textIpToNumericalIp( DccCommon::getOwnIp( server ) );
server->requestDccChat( m_partnerNick, ownNumericalIp, TQString::number( m_ownPort ) );
}
else
{
connectToPartner();
}
kdDebug() << "DccChat::DccChat() [END]" << endl;
updateAppearance();
}
DccChat::~DccChat()
{
kdDebug() << "DccChat::~DccChat()" << endl;
if(m_dccSocket)
m_dccSocket->close();
if(m_listenSocket)
m_listenSocket->close();
}
void DccChat::listenForPartner()
{
kdDebug() << "DccChat::listenForPartner() [BEGIN]" << endl;
// Set up server socket
TQString failedReason;
if ( Preferences::dccSpecificChatPorts() )
m_listenSocket = DccCommon::createServerSocketAndListen( TQT_TQOBJECT(this), &failedReason, Preferences::dccChatPortsFirst(), Preferences::dccChatPortsLast() );
else
m_listenSocket = DccCommon::createServerSocketAndListen( TQT_TQOBJECT(this), &failedReason );
if ( !m_listenSocket )
{
getTextView()->appendServerMessage( i18n( "DCC" ), i18n( "Could not open a socket for listening: %1" ).arg( failedReason ) );
return;
}
connect( m_listenSocket, TQT_SIGNAL(readyAccept()), this, TQT_SLOT(heardPartner()) );
// Get our own port number
m_ownPort = DccCommon::getServerSocketPort( m_listenSocket );
kdDebug() << "DccChat::listenForPartner(): using port " << m_ownPort << endl;
getTextView()->appendServerMessage( i18n("DCC"), i18n("Offering DCC Chat connection to %1 on port %2...").arg( m_partnerNick ).arg( m_ownPort ) );
m_sourceLine->setText(i18n( "DCC chat with %1 on port %2." ).arg( m_partnerNick ).arg( m_ownPort ) );
kdDebug() << "DccChat::listenForPartner() [END]" << endl;
}
void DccChat::connectToPartner()
{
TQHostAddress ip;
ip.setAddress(m_partnerHost.toUInt());
m_partnerHost=ip.toString();
getTextView()->appendServerMessage( i18n( "DCC" ), i18n( "%1 = nickname, %2 = IP, %3 = port",
"Establishing DCC Chat connection to %1 (%2:%3)..." ).arg( m_partnerNick ).arg( m_partnerHost ).arg( m_partnerPort ) );
m_sourceLine->setText( i18n( "%1 = nickname, %2 = IP, %3 = port", "DCC chat with %1 on %2:%3." ).arg( m_partnerNick ).arg( host ).arg( m_partnerPort ) );
m_dccSocket = new KNetwork::KStreamSocket( m_partnerHost, TQString::number( m_partnerPort ), TQT_TQOBJECT(this) );
m_dccSocket->setBlocking(false);
m_dccSocket->setFamily(KNetwork::KResolver::InetFamily);
m_dccSocket->enableRead(false);
m_dccSocket->enableWrite(false);
m_dccSocket->setTimeout(10000);
m_dccSocket->blockSignals(false);
connect( m_dccSocket, TQT_SIGNAL( hostFound() ), this, TQT_SLOT( lookupFinished() ) );
connect( m_dccSocket, TQT_SIGNAL( connected( const KResolverEntry& ) ), this, TQT_SLOT( dccChatConnectionSuccess() ) );
connect( m_dccSocket, TQT_SIGNAL( gotError( int ) ), this, TQT_SLOT( dccChatBroken( int ) ) );
connect( m_dccSocket, TQT_SIGNAL( readyRead() ), this, TQT_SLOT( readData() ) );
connect( m_dccSocket, TQT_SIGNAL( closed() ), this, TQT_SLOT( socketClosed() ) );
m_dccSocket->connect();
#if 0
//getTextView()->appendServerMessage(i18n("DCC"),i18n("Looking for host %1...").arg(host));
#endif
}
void DccChat::lookupFinished()
{
#if 0
//getTextView()->appendServerMessage(i18n("DCC"),i18n("Host found, connecting..."));
#endif
}
void DccChat::dccChatConnectionSuccess()
{
getTextView()->appendServerMessage( i18n( "DCC" ), i18n( "Established DCC Chat connection to %1." ).arg( m_partnerNick ) );
m_dccSocket->enableRead(true);
m_dccChatInput->setEnabled(true);
}
void DccChat::dccChatBroken(int error)
{
getTextView()->appendServerMessage(i18n("Error"),i18n("Connection broken, error code %1.").arg(error));
m_dccSocket->enableRead(false);
m_dccSocket->blockSignals(true);
m_dccSocket->close();
}
void DccChat::readData()
{
kdDebug() << k_funcinfo << ( m_listenSocket == 0 ) << " BEGIN" << endl;
int available=0;
int actual=0;
char* buffer=0;
TQString line;
TQTextCodec* codec = Konversation::IRCCharsets::self()->codecForName(m_encoding.isEmpty() ? Konversation::IRCCharsets::self()->encodingForLocale() : m_encoding);
available = m_dccSocket->bytesAvailable();
if( available > 0 )
{
buffer = new char[ available + 1 ];
actual = m_dccSocket->readBlock( buffer, available );
buffer[ actual ] = 0;
line.append( codec->toUnicode( buffer ) );
delete[] buffer;
TQStringList lines = TQStringList::split( '\n', line );
for( TQStringList::iterator itLine = lines.begin() ; itLine != lines.end() ; itLine++ )
{
if( (*itLine).startsWith( "\x01" ) )
{
// cut out the CTCP command
TQString ctcp = (*itLine).mid( 1, (*itLine).find( 1, 1 ) - 1 );
TQString ctcpCommand = ctcp.section( " ", 0, 0 );
TQString ctcpArgument = ctcp.section( " ", 1 );
if( ctcpCommand.lower() == "action" )
appendAction( m_partnerNick, ctcpArgument );
else
getTextView()->appendServerMessage( i18n( "CTCP" ), i18n( "Received unknown CTCP-%1 request from %2" ).arg( ctcp ).arg( m_partnerNick ) );
}
else getTextView()->append( m_partnerNick, *itLine );
} // endfor
} else {
dccChatBroken(m_dccSocket->error());
}
kdDebug() << k_funcinfo << " END" << endl;
}
void DccChat::dccChatTextEntered()
{
TQString line = m_dccChatInput->text();
m_dccChatInput->setText("");
if ( line.lower() == Preferences::commandChar()+"clear" )
{
textView->clear();
}
else if ( !line.isEmpty() )
{
sendDccChatText(line);
}
}
void DccChat::sendDccChatText(const TQString& sendLine)
{
kdDebug() << k_funcinfo << " BEGIN" << endl;
// create a work copy
TQString output(sendLine);
TQString cc=Preferences::commandChar();
if(!output.isEmpty())
{
TQStringList lines = TQStringList::split('\n',output);
// wrap socket into a stream
TQTextStream stream(m_dccSocket);
// init stream props
stream.setCodec(Konversation::IRCCharsets::self()->codecForName(m_encoding.isEmpty() ? Konversation::IRCCharsets::self()->encodingForLocale() : m_encoding));
for( TQStringList::iterator itLine = lines.begin() ; itLine != lines.end() ; itLine++ )
{
TQString line( *itLine );
// replace aliases and wildcards
// if(filter.replaceAliases(line)) line=server->parseWildcards(line,nick,getName(),TQString(),TQString(),TQString());
// line=filter.parse(nick,line,getName());
// convert /me actions
TQString cmd=line.section(' ', 0,0).lower();
if (cmd == cc+"me")
{
appendAction( m_ownNick, line.section( " ", 1 ) );
line=TQString("\x01%1 %2\x01").arg("ACTION").arg(line.section(" ",1));
}
else if (cmd == cc+"close")
{
closeYourself(false);
return;
}
else
getTextView()->append( m_ownNick, line );
stream << line << endl;
} // endfor
// detach stream
stream.unsetDevice();
}
kdDebug() << k_funcinfo << " END" << endl;
}
void DccChat::heardPartner()
{
m_dccSocket = static_cast<KNetwork::KStreamSocket*>( m_listenSocket->accept() );
if( !m_dccSocket )
{
getTextView()->appendServerMessage( i18n( "Error" ),i18n( "Could not accept the client." ) );
return;
}
connect( m_dccSocket, TQT_SIGNAL( readyRead() ), this, TQT_SLOT( readData() ) );
connect( m_dccSocket, TQT_SIGNAL( closed() ), this, TQT_SLOT( socketClosed() ) );
connect( m_dccSocket, TQT_SIGNAL( gotError( int ) ), this, TQT_SLOT( dccChatBroken( int ) ) );
// the listen socket isn't needed anymore
disconnect( m_listenSocket, 0, 0, 0 );
m_listenSocket->close();
m_listenSocket = 0;
m_dccSocket->enableRead(true);
m_dccChatInput->setEnabled(true);
getTextView()->appendServerMessage( i18n( "DCC" ), i18n( "Established DCC Chat connection to %1." ).arg( m_partnerNick ) );
}
void DccChat::socketClosed()
{
getTextView()->appendServerMessage(i18n("DCC"),"Connection closed.");
m_dccChatInput->setEnabled(false);
m_dccSocket = 0;
}
void DccChat::textPasted(const TQString& text)
{
sendDccChatText(text);
}
void DccChat::childAdjustFocus()
{
m_dccChatInput->setFocus();
}
bool DccChat::canBeFrontView()
{
return true;
}
bool DccChat::searchView()
{
return true;
}
int DccChat::getOwnPort()
{
return m_ownPort;
}
TQString DccChat::getTextInLine()
{
return m_dccChatInput->text();
}
void DccChat::appendInputText( const TQString& s, bool fromCursor )
{
if(!fromCursor)
{
m_dccChatInput->append(s);
}
else
{
int para = 0, index = 0;
m_dccChatInput->getCursorPosition(&para, &index);
m_dccChatInput->insertAt(s, para, index);
m_dccChatInput->setCursorPosition(para, index + s.length());
}
}
//FIXME uh... where is the confimation for this?
bool DccChat::closeYourself(bool)
{
deleteLater();
return true;
}
void DccChat::setChannelEncoding(const TQString& encoding) // virtual
{
m_encoding = encoding;
}
TQString DccChat::getChannelEncoding() // virtual
{
return m_encoding;
}
TQString DccChat::getChannelEncodingDefaultDesc() // virtual
{
return i18n("Default ( %1 )").arg(Konversation::IRCCharsets::self()->encodingForLocale());
}
void DccChat::showEvent(TQShowEvent* /* event */)
{
if(m_initialShow) {
m_initialShow = false;
TQValueList<int> sizes;
sizes << m_sourceLine->sizeHint().height() << (height() - m_sourceLine->sizeHint().height());
m_headerSplitter->setSizes(sizes);
}
}
void DccChat::updateAppearance()
{
TQColor fg;
TQColor bg;
if(Preferences::inputFieldsBackgroundColor())
{
fg=Preferences::color(Preferences::ChannelMessage);
bg=Preferences::color(Preferences::TextViewBackground);
}
else
{
fg=colorGroup().foreground();
bg=colorGroup().base();
}
m_dccChatInput->unsetPalette();
m_dccChatInput->setPaletteForegroundColor(fg);
m_dccChatInput->setPaletteBackgroundColor(bg);
getTextView()->unsetPalette();
if(Preferences::showBackgroundImage())
{
getTextView()->setViewBackground(Preferences::color(Preferences::TextViewBackground),
Preferences::backgroundImage());
}
else
{
getTextView()->setViewBackground(Preferences::color(Preferences::TextViewBackground),
TQString());
}
if (Preferences::customTextFont())
{
getTextView()->setFont(Preferences::textFont());
m_dccChatInput->setFont(Preferences::textFont());
}
else
{
getTextView()->setFont(TDEGlobalSettings::generalFont());
m_dccChatInput->setFont(TDEGlobalSettings::generalFont());
}
ChatWindow::updateAppearance();
}
#include "dccchat.moc"