/* 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 Copyright (C) 2006 Eike Hein Copyright (C) 2004,2007 Shintaro Matsuoka */ #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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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( 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(¶, &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 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"